diff --git a/lib/instrumentation/nextjs/utils.js b/lib/instrumentation/nextjs/utils.js
index 95c0645a32..3e0aef73f0 100644
--- a/lib/instrumentation/nextjs/utils.js
+++ b/lib/instrumentation/nextjs/utils.js
@@ -56,16 +56,3 @@ utils.isMiddlewareInstrumentationSupported = function isMiddlewareInstrumentatio
     semver.gte(version, MIN_MW_SUPPORTED_VERSION) && semver.lte(version, MAX_MW_SUPPORTED_VERSION)
   )
 }
-
-/**
- * Depending on the Next.js version the segment tree varies as it adds setTimeout segments.
- * This util will find the segment that has `getServerSideProps` in the name
- *
- * @param {object} rootSegment trace root
- * @returns {object} getServerSideProps segment
- */
-utils.getServerSidePropsSegment = function getServerSidePropsSegment(rootSegment) {
-  return rootSegment.children[0].children.find((segment) =>
-    segment.name.includes('getServerSideProps')
-  )
-}
diff --git a/lib/metrics/recorders/custom.js b/lib/metrics/recorders/custom.js
index c36cebe44b..f4623202e1 100644
--- a/lib/metrics/recorders/custom.js
+++ b/lib/metrics/recorders/custom.js
@@ -9,7 +9,7 @@ const NAMES = require('../names')
 
 function record(segment, scope, transaction) {
   const duration = segment.getDurationInMillis()
-  const exclusive = segment.getExclusiveDurationInMillis()
+  const exclusive = segment.getExclusiveDurationInMillis(transaction.trace)
   const name = NAMES.CUSTOM + NAMES.ACTION_DELIMITER + segment.name
 
   if (scope) {
diff --git a/lib/metrics/recorders/database-operation.js b/lib/metrics/recorders/database-operation.js
index 358e9e98d1..c602aec2b2 100644
--- a/lib/metrics/recorders/database-operation.js
+++ b/lib/metrics/recorders/database-operation.js
@@ -22,7 +22,7 @@ const metrics = require('../names')
  */
 function recordOperationMetrics(segment, scope, transaction) {
   const duration = segment.getDurationInMillis()
-  const exclusive = segment.getExclusiveDurationInMillis()
+  const exclusive = segment.getExclusiveDurationInMillis(transaction.trace)
   const type = transaction.isWeb() ? 'allWeb' : 'allOther'
   const operation = segment.name
 
diff --git a/lib/metrics/recorders/database.js b/lib/metrics/recorders/database.js
index 66eec2664f..1fbd40c098 100644
--- a/lib/metrics/recorders/database.js
+++ b/lib/metrics/recorders/database.js
@@ -15,9 +15,8 @@ const { DESTINATIONS } = require('../../config/attribute-filter')
  */
 
 function recordQueryMetrics(segment, scope, transaction) {
-  debugger
   const duration = segment.getDurationInMillis()
-  const exclusive = segment.getExclusiveDurationInMillis()
+  const exclusive = segment.getExclusiveDurationInMillis(transaction.trace)
   const type = transaction.isWeb() ? DB.WEB : DB.OTHER
   const thisTypeSlash = this.type + '/'
   const operation = DB.OPERATION + '/' + thisTypeSlash + this.operation
diff --git a/lib/metrics/recorders/generic.js b/lib/metrics/recorders/generic.js
index f46293c16c..945b61c3c6 100644
--- a/lib/metrics/recorders/generic.js
+++ b/lib/metrics/recorders/generic.js
@@ -7,7 +7,7 @@
 
 function record(segment, scope, transaction) {
   const duration = segment.getDurationInMillis()
-  const exclusive = segment.getExclusiveDurationInMillis()
+  const exclusive = segment.getExclusiveDurationInMillis(transaction.trace)
 
   if (scope) {
     transaction.measure(segment.name, scope, duration, exclusive)
diff --git a/lib/metrics/recorders/http.js b/lib/metrics/recorders/http.js
index 777424f6bf..4ef57aff07 100644
--- a/lib/metrics/recorders/http.js
+++ b/lib/metrics/recorders/http.js
@@ -23,7 +23,7 @@ function recordWeb(segment, scope, tx) {
 
   const duration = segment.getDurationInMillis()
   const totalTime = tx.trace.getTotalTimeDurationInMillis()
-  const exclusive = segment.getExclusiveDurationInMillis()
+  const exclusive = segment.getExclusiveDurationInMillis(tx.trace)
   const partial = segment.partialName
   const config = tx.agent.config
   // named / key transaction support requires per-name apdexT
diff --git a/lib/metrics/recorders/http_external.js b/lib/metrics/recorders/http_external.js
index 252a97f1f0..0a656b407f 100644
--- a/lib/metrics/recorders/http_external.js
+++ b/lib/metrics/recorders/http_external.js
@@ -10,7 +10,7 @@ const EXTERNAL = require('../../metrics/names').EXTERNAL
 function recordExternal(host, library) {
   return function externalRecorder(segment, scope, transaction) {
     const duration = segment.getDurationInMillis()
-    const exclusive = segment.getExclusiveDurationInMillis()
+    const exclusive = segment.getExclusiveDurationInMillis(transaction.trace)
     const metricName = EXTERNAL.PREFIX + host + '/' + library
     const rollupType = transaction.isWeb() ? EXTERNAL.WEB : EXTERNAL.OTHER
     const rollupHost = EXTERNAL.PREFIX + host + '/all'
diff --git a/lib/metrics/recorders/message-transaction.js b/lib/metrics/recorders/message-transaction.js
index c880226b1d..740223d867 100644
--- a/lib/metrics/recorders/message-transaction.js
+++ b/lib/metrics/recorders/message-transaction.js
@@ -13,7 +13,7 @@ function recordMessageTransaction(segment, scope, tx) {
   }
 
   const duration = segment.getDurationInMillis()
-  const exclusive = segment.getExclusiveDurationInMillis()
+  const exclusive = segment.getExclusiveDurationInMillis(tx.trace)
   const totalTime = tx.trace.getTotalTimeDurationInMillis()
 
   if (scope) {
diff --git a/lib/metrics/recorders/middleware.js b/lib/metrics/recorders/middleware.js
index d8ec3e0493..57adfd0096 100644
--- a/lib/metrics/recorders/middleware.js
+++ b/lib/metrics/recorders/middleware.js
@@ -16,7 +16,7 @@
 function makeMiddlewareRecorder(_shim, metricName) {
   return function middlewareMetricRecorder(segment, scope, transaction) {
     const duration = segment.getDurationInMillis()
-    const exclusive = segment.getExclusiveDurationInMillis()
+    const exclusive = segment.getExclusiveDurationInMillis(transaction.trace)
 
     if (scope) {
       transaction.measure(metricName, scope, duration, exclusive)
diff --git a/lib/metrics/recorders/other.js b/lib/metrics/recorders/other.js
index 0637ccaaa9..47f0c642c1 100644
--- a/lib/metrics/recorders/other.js
+++ b/lib/metrics/recorders/other.js
@@ -15,7 +15,7 @@ function recordBackground(segment, scope, tx) {
   }
 
   const duration = segment.getDurationInMillis()
-  const exclusive = segment.getExclusiveDurationInMillis()
+  const exclusive = segment.getExclusiveDurationInMillis(tx.trace)
   const totalTime = tx.trace.getTotalTimeDurationInMillis()
   const name = segment.partialName
 
diff --git a/lib/shim/shim.js b/lib/shim/shim.js
index 3a9e5ffa62..120a9f6e3a 100644
--- a/lib/shim/shim.js
+++ b/lib/shim/shim.js
@@ -1325,8 +1325,7 @@ function createSegment(name, recorder, parent) {
  * @param {Shim} params.shim instance of shim
  * @param {Transaction} params.transaction active transaction
  * @param {TraceSegment} params.parent the segment that will be the parent of the newly created segment
- * @param params.spec
- * @param {string|specs.SegmentSpec} spec options for creating segment
+ * @param {string|specs.SegmentSpec} params.spec options for creating segment
  * @returns {?TraceSegment} A new trace segment if a transaction is active, else
  *  `null` is returned.
  */
diff --git a/lib/transaction/trace/exclusive-time-calculator.js b/lib/transaction/trace/exclusive-time-calculator.js
index fd2fb022ec..9d01a1c90c 100644
--- a/lib/transaction/trace/exclusive-time-calculator.js
+++ b/lib/transaction/trace/exclusive-time-calculator.js
@@ -6,7 +6,9 @@
 'use strict'
 
 class ExclusiveCalculator {
-  constructor(root) {
+  constructor(root, trace) {
+    this.trace = trace
+    this.id = root.id
     this.toProcess = [root]
     // use a second stack to do a post-order traversal
     this.parentStack = []
@@ -19,7 +21,7 @@ class ExclusiveCalculator {
   process() {
     while (this.toProcess.length) {
       const segment = this.toProcess.pop()
-      const children = segment.getChildren()
+      const children = this.trace.getChildren(segment.id)
       // when we hit a leaf, calc the exclusive time and report the time
       // range to the parent
       if (children.length === 0) {
diff --git a/lib/transaction/trace/index.js b/lib/transaction/trace/index.js
index c7c22121c1..da4c1dceb2 100644
--- a/lib/transaction/trace/index.js
+++ b/lib/transaction/trace/index.js
@@ -41,7 +41,7 @@ function Trace(transaction) {
   this.root.start()
 
   this.intrinsics = Object.create(null)
-  this.segmentsSeen = 0
+  this.segments = []
   this.totalTimeCache = null
 
   this.custom = new Attributes(ATTRIBUTE_SCOPE, MAXIMUM_CUSTOM_ATTRIBUTES)
@@ -63,16 +63,11 @@ function Trace(transaction) {
  * segments that support recording.
  */
 Trace.prototype.end = function end() {
-  const segments = [this.root]
+  this.root.finalize(this)
+  const segments = this.segments
 
-  while (segments.length) {
-    const segment = segments.pop()
-    segment.finalize()
-
-    const children = segment.getChildren()
-    for (let i = 0; i < children.length; ++i) {
-      segments.push(children[i])
-    }
+  for (let i = 0; i < segments.length; i++) {
+    segments[i].finalize(this)
   }
 }
 
@@ -85,14 +80,13 @@ Trace.prototype.generateSpanEvents = function generateSpanEvents() {
   if (!shouldGenerateSpanEvents(config, this.transaction)) {
     return
   }
-  const toProcess = []
 
   // Root segment does not become a span, so we need to process it separately.
   const spanAggregator = this.transaction.agent.spanEventAggregator
 
-  const children = this.root.getChildren()
+  const segments = this.segments
 
-  if (children.length > 0) {
+  if (segments.length > 0) {
     // At the point where these attributes are available, we only have a
     // root span. Adding attributes to first non-root span here.
     const attributeMap = {
@@ -106,41 +100,28 @@ Trace.prototype.generateSpanEvents = function generateSpanEvents() {
 
     for (const [key, value] of Object.entries(attributeMap)) {
       if (value !== null) {
-        children[0].addSpanAttribute(key, value)
+        segments[0].addSpanAttribute(key, value)
       }
     }
   }
 
-  for (let i = 0; i < children.length; ++i) {
-    toProcess.push(new DTTraceNode(children[i], this.transaction.parentSpanId, true))
-  }
-
-  while (toProcess.length) {
-    const segmentInfo = toProcess.pop()
-    const segment = segmentInfo.segment
-
+  for (let i = 0; i < segments.length; ++i) {
+    const segment = segments[i]
+    const isRoot = segment.parentId === this.root.id
+    const parentId = isRoot ? this.transaction.parentSpanId : segment.parentId
     // Even though at some point we might want to stop adding events because all the priorities
     // should be the same, we need to count the spans as seen.
     spanAggregator.addSegment({
       segment,
       transaction: this.transaction,
-      parentId: segmentInfo.parentId,
-      isRoot: segmentInfo.isRoot
+      parentId,
+      isRoot
     })
-
-    const nodes = segment.getChildren()
-    for (let i = 0; i < nodes.length; ++i) {
-      const node = new DTTraceNode(nodes[i], segment.id)
-      toProcess.push(node)
-    }
   }
 }
 
 function shouldGenerateSpanEvents(config, txn) {
-  if (!config.distributed_tracing.enabled) {
-    return false
-  }
-  if (!config.span_events.enabled) {
+  if (!(config.distributed_tracing.enabled && config.span_events.enabled)) {
     return false
   }
 
@@ -148,12 +129,6 @@ function shouldGenerateSpanEvents(config, txn) {
   return infiniteTracingConfigured || txn.sampled
 }
 
-function DTTraceNode(segment, parentId, isRoot = false) {
-  this.segment = segment
-  this.parentId = parentId
-  this.isRoot = isRoot
-}
-
 /**
  * Add a child to the list of segments.
  *
@@ -220,7 +195,7 @@ Trace.prototype.addCustomAttribute = function addCustomAttribute(key, value) {
  *                   traces, in milliseconds.
  */
 Trace.prototype.getExclusiveDurationInMillis = function getExclusiveDurationInMillis() {
-  return this.root.getExclusiveDurationInMillis()
+  return this.root.getExclusiveDurationInMillis(this)
 }
 
 /**
@@ -234,16 +209,14 @@ Trace.prototype.getTotalTimeDurationInMillis = function getTotalTimeDurationInMi
   if (this.totalTimeCache !== null) {
     return this.totalTimeCache
   }
-  if (this.root.children.length === 0) {
+  const segments = this.segments
+  if (segments.length === 0) {
     return 0
   }
-  const segments = this.root.getChildren()
-  let totalTimeInMillis = 0
 
-  while (segments.length !== 0) {
-    const segment = segments.pop()
-    totalTimeInMillis += segment.getExclusiveDurationInMillis()
-    segment.getChildren().forEach((childSegment) => segments.push(childSegment))
+  let totalTimeInMillis = 0
+  for (let i = 0; i < segments.length; i++) {
+    totalTimeInMillis += segments[i].getExclusiveDurationInMillis(this)
   }
 
   if (!this.transaction.isActive()) {
@@ -366,6 +339,104 @@ Trace.prototype._getRequestUri = function _getRequestUri() {
   return requestUri
 }
 
+/**
+ * Gets all children of a segment.
+ *
+ * @param {number} id of segment
+ * @returns {Array.<TraceSegment>} list of all segments that have the parentId of the segment
+ */
+Trace.prototype.getChildren = function getChildren(id) {
+  return this.segments.filter((segment) => segment.parentId === id)
+}
+
+/**
+ * Gets all children of a segment that should be collected and not ignored.
+ *
+ * @param {number} id of segment
+ * @returns {Array.<TraceSegment>} list of all segments that have the parentId of the segment
+ */
+Trace.prototype.getCollectedChildren = function getCollectedChildren(id) {
+  return this.segments.filter(
+    (segment) => segment.parentId === id && segment._collect && !segment.ignore
+  )
+}
+
+/**
+ * Gets the parent segment from list of segments on trace by passing in the `parentId`
+ * and matching on the `segment.id`
+ *
+ * @param {number} parentId id of parent segment you want to retrieve
+ * @returns {TraceSegment} parent segment
+ */
+Trace.prototype.getParent = function getParent(parentId) {
+  return this.segments.filter((segment) => segment.id === parentId)[0]
+}
+
+/**
+ * This is perhaps the most poorly-documented element of transaction traces:
+ * what do each of the segment representations look like prior to encoding?
+ * Spelunking in the code for the other agents has revealed that each child
+ * node is an array with the following field in the following order:
+ *
+ * 0: entry timestamp relative to transaction start time
+ * 1: exit timestamp
+ * 2: metric name
+ * 3: parameters as a name -> value JSON dictionary
+ * 4: any child segments
+ *
+ * Other agents include further fields in this. I haven't gotten to the bottom
+ * of all of them (and Ruby, of course, sends marshalled Ruby object), but
+ * here's what I know so far:
+ *
+ * in Java:
+ * 5: class name
+ * 6: method name
+ *
+ * in Python:
+ * 5: a "label"
+ *
+ * FIXME: I don't know if it makes sense to add custom fields for Node. TBD
+ */
+Trace.prototype.toJSON = function toJSON() {
+  // use depth-first search on the segment tree using stack
+  const resultDest = []
+  // array of objects relating a segment and the destination for its
+  // serialized data.
+  const segmentsToProcess = [
+    {
+      segment: this.root,
+      destination: resultDest
+    }
+  ]
+
+  while (segmentsToProcess.length !== 0) {
+    const { segment, destination } = segmentsToProcess.pop()
+    const start = segment.timer.startedRelativeTo(this.root.timer)
+    const duration = segment.getDurationInMillis()
+
+    const segmentChildren = this.getCollectedChildren(segment.id)
+    const childArray = []
+
+    // push serialized data into the specified destination
+    destination.push([start, start + duration, segment.name, segment.getAttributes(), childArray])
+
+    if (segmentChildren.length) {
+      // push the children and the parent's children array into the stack.
+      // to preserve the chronological order of the children, push them
+      // onto the stack backwards (so the first one created is on top).
+      for (let i = segmentChildren.length - 1; i >= 0; --i) {
+        segmentsToProcess.push({
+          segment: segmentChildren[i],
+          destination: childArray
+        })
+      }
+    }
+  }
+
+  // pull the result out of the array we serialized it into
+  return resultDest[0]
+}
+
 /**
  * Serializes the trace into the expected JSON format to be sent.
  *
@@ -379,17 +450,21 @@ Trace.prototype._serializeTrace = function _serializeTrace() {
     intrinsics: this.intrinsics
   }
 
-  return [
+  const trace = [
     this.root.timer.start * FROM_MILLIS,
     {}, // moved to agentAttributes
     {
       // hint to RPM for how to display this trace's segments
       nr_flatten_leading: false
     }, // moved to userAttributes
-    this.root.toJSON(),
+    this.toJSON(),
     attributes,
     [] // FIXME: parameter groups
   ]
+
+  // clear out segments
+  this.segments = []
+  return trace
 }
 
 module.exports = Trace
diff --git a/lib/transaction/trace/segment.js b/lib/transaction/trace/segment.js
index bc3f0f9bed..a03c7b6e59 100644
--- a/lib/transaction/trace/segment.js
+++ b/lib/transaction/trace/segment.js
@@ -6,7 +6,6 @@
 'use strict'
 
 const { DESTINATIONS } = require('../../config/attribute-filter')
-const logger = require('../../logger').child({ component: 'segment' })
 const Timer = require('../../timer')
 const hashes = require('../../util/hashes')
 
@@ -34,20 +33,21 @@ const ATTRIBUTE_SCOPE = 'segment'
  * @param {object} params.config agent config
  * @param {string} params.name Human-readable name for this segment (e.g. 'http', 'net', 'express',
  *  'mysql', etc).
+ *  @param {number} params.parentId parent id of segment
  * @param {boolean} params.collect flag to collect as part of transaction trace
  * @param {TraceSegment} params.root root segment
  * @param {boolean} params.isRoot flag to indicate it is the root segment
  */
-function TraceSegment({ config, name, collect, root, isRoot = false }) {
+function TraceSegment({ config, name, collect, parentId, root, isRoot = false }) {
   this.isRoot = isRoot
   this.root = root
   this.name = name
   this.attributes = new Attributes(ATTRIBUTE_SCOPE)
-  this.children = []
   this.spansEnabled = config?.distributed_tracing?.enabled && config?.span_events?.enabled
 
   // Generate a unique id for use in span events.
   this.id = hashes.makeId()
+  this.parentId = parentId
   this.timer = new Timer()
 
   this.internal = false
@@ -173,7 +173,7 @@ TraceSegment.prototype.end = function end() {
   this._updateRootTimer()
 }
 
-TraceSegment.prototype.finalize = function finalize() {
+TraceSegment.prototype.finalize = function finalize(trace) {
   if (this.timer.softEnd()) {
     this._updateRootTimer()
     // timer.softEnd() returns true if the timer was ended prematurely, so
@@ -181,7 +181,7 @@ TraceSegment.prototype.finalize = function finalize() {
     this.name = NAMES.TRUNCATED.PREFIX + this.name
   }
 
-  this.addAttribute('nr_exclusive_duration_millis', this.getExclusiveDurationInMillis())
+  this.addAttribute('nr_exclusive_duration_millis', this.getExclusiveDurationInMillis(trace))
 }
 
 /**
@@ -206,36 +206,6 @@ TraceSegment.prototype._isEnded = function _isEnded() {
   return !this.timer.isActive() || this.timer.touched
 }
 
-/**
- * Add a new segment to a parent(scope) implicitly bounded by this segment.
- *
- * @param {object} params to function
- * @param {object} params.config agent config
- * @param {string} params.name Human-readable name for this segment (e.g. 'http', 'net', 'express',
- *  'mysql', etc).
- * @param {boolean} params.collect flag to collect as part of transaction trace
- * @param {TraceSegment} params.root root segment
- * @returns {TraceSegment} New nested TraceSegment.
- */
-TraceSegment.prototype.add = function add({ config, name, collect, root }) {
-  // this is needed here to check when add is called directly on segment
-  if (this.opaque) {
-    logger.trace('Skipping child addition on opaque segment')
-    return this
-  }
-
-  const segment = new TraceSegment({ config, name, collect, root })
-
-  this.children.push(segment)
-
-  // This should only be used in testing
-  if (config.debug && config.debug.double_linked_transactions) {
-    segment.parent = this
-  }
-
-  return segment
-}
-
 /**
  * Set the duration of the segment explicitly.
  *
@@ -271,139 +241,15 @@ function _setExclusiveDurationInMillis(duration) {
  */
 TraceSegment.prototype.getExclusiveDurationInMillis = getExclusiveDurationInMillis
 
-function getExclusiveDurationInMillis() {
+function getExclusiveDurationInMillis(trace) {
   if (this._exclusiveDuration == null) {
     // Calculate the exclusive time for the subtree rooted at `this`
-    const calculator = new ExclusiveCalculator(this)
+    const calculator = new ExclusiveCalculator(this, trace)
     calculator.process()
   }
   return this._exclusiveDuration
 }
 
-TraceSegment.prototype.getChildren = function getChildren() {
-  const children = []
-  for (let i = 0, len = this.children.length; i < len; ++i) {
-    if (!this.children[i].ignore) {
-      children.push(this.children[i])
-    }
-  }
-  return children
-}
-
-TraceSegment.prototype.getCollectedChildren = function getCollectedChildren() {
-  const children = []
-  for (let i = 0, len = this.children.length; i < len; ++i) {
-    if (this.children[i]._collect && !this.children[i].ignore) {
-      children.push(this.children[i])
-    }
-  }
-  return children
-}
-
-/**
- * Enumerate the timings of this segment's descendants.
- *
- * @param {number} end The end of this segment, to keep the calculated
- *                     duration from exceeding the duration of the
- *                     parent. Defaults to Infinity.
- * @returns {Array} Unsorted list of [start, end] pairs, with no pair
- *                  having an end greater than the passed in end time.
- */
-TraceSegment.prototype._getChildPairs = function _getChildPairs(end) {
-  // quick optimization
-  if (this.children.length < 1) {
-    return []
-  }
-  if (!end) {
-    end = Infinity
-  }
-
-  let children = this.getChildren()
-  const childPairs = []
-  while (children.length) {
-    const child = children.pop()
-    const pair = child.timer.toRange()
-
-    if (pair[0] >= end) {
-      continue
-    }
-
-    children = children.concat(child.getChildren())
-
-    pair[1] = Math.min(pair[1], end)
-    childPairs.push(pair)
-  }
-
-  return childPairs
-}
-
-/**
- * This is perhaps the most poorly-documented element of transaction traces:
- * what do each of the segment representations look like prior to encoding?
- * Spelunking in the code for the other agents has revealed that each child
- * node is an array with the following field in the following order:
- *
- * 0: entry timestamp relative to transaction start time
- * 1: exit timestamp
- * 2: metric name
- * 3: parameters as a name -> value JSON dictionary
- * 4: any child segments
- *
- * Other agents include further fields in this. I haven't gotten to the bottom
- * of all of them (and Ruby, of course, sends marshalled Ruby object), but
- * here's what I know so far:
- *
- * in Java:
- * 5: class name
- * 6: method name
- *
- * in Python:
- * 5: a "label"
- *
- * FIXME: I don't know if it makes sense to add custom fields for Node. TBD
- */
-TraceSegment.prototype.toJSON = function toJSON() {
-  const root = this.isRoot ? this : this.root
-  // use depth-first search on the segment tree using stack
-  const resultDest = []
-  // array of objects relating a segment and the destination for its
-  // serialized data.
-  const segmentsToProcess = [
-    {
-      segment: this,
-      destination: resultDest
-    }
-  ]
-
-  while (segmentsToProcess.length !== 0) {
-    const { segment, destination } = segmentsToProcess.pop()
-
-    const start = segment.timer.startedRelativeTo(root.timer)
-    const duration = segment.getDurationInMillis()
-
-    const segmentChildren = segment.getCollectedChildren()
-    const childArray = []
-
-    // push serialized data into the specified destination
-    destination.push([start, start + duration, segment.name, segment.getAttributes(), childArray])
-
-    if (segmentChildren.length) {
-      // push the children and the parent's children array into the stack.
-      // to preserve the chronological order of the children, push them
-      // onto the stack backwards (so the first one created is on top).
-      for (let i = segmentChildren.length - 1; i >= 0; --i) {
-        segmentsToProcess.push({
-          segment: segmentChildren[i],
-          destination: childArray
-        })
-      }
-    }
-  }
-
-  // pull the result out of the array we serialized it into
-  return resultDest[0]
-}
-
 /**
  * Adds all the relevant segment attributes for an External http request
  *
diff --git a/lib/transaction/tracer/index.js b/lib/transaction/tracer/index.js
index 72e789d1bf..f61330880d 100644
--- a/lib/transaction/tracer/index.js
+++ b/lib/transaction/tracer/index.js
@@ -13,6 +13,7 @@ const SKIP_WRAPPING_FUNCTION_MESSAGE = 'Not wrapping "%s" because it was not a f
 const CREATE_SEGMENT_MESSAGE = 'Creating "%s" segment for transaction %s.'
 const { addCLMAttributes: maybeAddCLMAttributes } = require('../../util/code-level-metrics')
 const AsyncLocalContextManager = require('../../context-manager/async-local-context-manager')
+const TraceSegment = require('../trace/segment')
 
 module.exports = Tracer
 
@@ -101,22 +102,24 @@ function createSegment({ name, recorder, parent, transaction }) {
   logger.trace('Adding segment %s to %s in %s', name, parent.name, transaction.id)
 
   let collect = true
-  if (transaction.trace.segmentsSeen++ >= this.agent.config.max_trace_segments) {
+  if (transaction.trace.segments.length >= this.agent.config.max_trace_segments) {
     collect = false
   }
 
   transaction.incrementCounters()
 
-  const segment = parent.add({
+  const segment = new TraceSegment({
     config: this.agent.config,
     name,
     collect,
-    root: transaction.trace.root
+    root: transaction.trace.root,
+    parentId: parent.id
   })
 
   if (recorder) {
     transaction.addRecorder(recorder.bind(null, segment))
   }
+  transaction.trace.segments.push(segment)
 
   return segment
 }
diff --git a/test/benchmark/shim/segments.bench.js b/test/benchmark/shim/segments.bench.js
index 3905e6b216..f4789cd3a7 100644
--- a/test/benchmark/shim/segments.bench.js
+++ b/test/benchmark/shim/segments.bench.js
@@ -94,7 +94,6 @@ suite.add({
   fn: function () {
     const test = shared.getTest()
     shim.createSegment('foo', test.func, tx.trace.root)
-    tx.trace.root.children = []
     return test
   }
 })
diff --git a/test/benchmark/trace/segment.bench.js b/test/benchmark/trace/segment.bench.js
index 6794b9f155..579748dca9 100644
--- a/test/benchmark/trace/segment.bench.js
+++ b/test/benchmark/trace/segment.bench.js
@@ -7,26 +7,20 @@
 
 const helper = require('../../lib/agent_helper')
 const benchmark = require('../../lib/benchmark')
-const Segment = require('../../../lib/transaction/trace/segment')
+const Transaction = require('../../../lib/transaction')
 
 const agent = helper.loadMockedAgent()
 const suite = benchmark.createBenchmark({
   name: 'trace segments'
 })
 
-let root
-
-function addChildren(rootSegment, numChildren) {
-  const queue = [rootSegment]
+let trace
+function addChildren(trace, numChildren) {
+  const queue = [trace.root]
   for (let numSegments = 1; numSegments < 900; numSegments += numChildren) {
     const parent = queue.shift()
     for (let i = 0; i < numChildren; ++i) {
-      const child = parent.add({
-        name: 'child ' + (numSegments + i),
-        root: rootSegment,
-        collect: true,
-        config: agent.config
-      })
+      const child = trace.add('child ' + (numSegments + i), null, parent)
       child.timer.setDurationInMillis(
         (0.99 + Math.random() / 100) * parent.timer.durationInMillis,
         parent.timer.start + 1
@@ -40,12 +34,13 @@ suite.add({
   name: 'toJSON flat',
 
   before: function buildTree() {
-    root = new Segment({ name: 'ROOT', isRoot: true, config: agent.config })
-    root.timer.setDurationInMillis(10000, Date.now())
-    addChildren(root, 899)
+    const transaction = new Transaction(agent)
+    trace = transaction.trace
+    trace.root.timer.setDurationInMillis(10000, Date.now())
+    addChildren(trace, 899)
   },
   fn: function () {
-    return root.toJSON()
+    return trace.toJSON()
   }
 })
 
@@ -53,12 +48,13 @@ suite.add({
   name: 'toJSON linear',
 
   before: function buildTree() {
-    root = new Segment({ name: 'ROOT', isRoot: true, config: agent.config })
-    root.timer.setDurationInMillis(10000, Date.now())
-    addChildren(root, 1)
+    const transaction = new Transaction(agent)
+    trace = transaction.trace
+    trace.root.timer.setDurationInMillis(10000, Date.now())
+    addChildren(trace, 1)
   },
   fn: function () {
-    return root.toJSON()
+    return trace.toJSON()
   }
 })
 
@@ -66,12 +62,13 @@ suite.add({
   name: 'toJSON binary',
 
   before: function buildTree() {
-    root = new Segment({ name: 'ROOT', isRoot: true, config: agent.config })
-    root.timer.setDurationInMillis(10000, Date.now())
-    addChildren(root, 2)
+    const transaction = new Transaction(agent)
+    trace = transaction.trace
+    trace.root.timer.setDurationInMillis(10000, Date.now())
+    addChildren(trace, 2)
   },
   fn: function () {
-    return root.toJSON()
+    return trace.toJSON()
   }
 })
 
@@ -79,12 +76,13 @@ suite.add({
   name: 'getExclusiveDurationInMillis flat',
 
   before: function buildTree() {
-    root = new Segment({ name: 'ROOT', isRoot: true, config: agent.config })
-    root.timer.setDurationInMillis(10000, Date.now())
-    addChildren(root, 899)
+    const transaction = new Transaction(agent)
+    trace = transaction.trace
+    trace.root.timer.setDurationInMillis(10000, Date.now())
+    addChildren(trace, 899)
   },
   fn: function () {
-    return root.getExclusiveDurationInMillis()
+    return trace.getExclusiveDurationInMillis()
   }
 })
 
@@ -92,12 +90,13 @@ suite.add({
   name: 'getExclusiveDurationInMillis linear',
 
   before: function buildTree() {
-    root = new Segment({ name: 'ROOT', isRoot: true, config: agent.config })
-    root.timer.setDurationInMillis(10000, Date.now())
-    addChildren(root, 1)
+    const transaction = new Transaction(agent)
+    trace = transaction.trace
+    trace.root.timer.setDurationInMillis(10000, Date.now())
+    addChildren(trace, 1)
   },
   fn: function () {
-    return root.getExclusiveDurationInMillis()
+    return trace.getExclusiveDurationInMillis()
   }
 })
 
@@ -105,12 +104,13 @@ suite.add({
   name: 'getExclusiveDurationInMillis binary',
 
   before: function buildTree() {
-    root = new Segment({ name: 'ROOT', isRoot: true, config: agent.config })
-    root.timer.setDurationInMillis(10000, Date.now())
-    addChildren(root, 2)
+    const transaction = new Transaction(agent)
+    trace = transaction.trace
+    trace.root.timer.setDurationInMillis(10000, Date.now())
+    addChildren(trace, 2)
   },
   fn: function () {
-    return root.getExclusiveDurationInMillis()
+    return trace.getExclusiveDurationInMillis()
   }
 })
 
diff --git a/test/integration/cat/cat.test.js b/test/integration/cat/cat.test.js
index d3faf8ec0e..c839171ad6 100644
--- a/test/integration/cat/cat.test.js
+++ b/test/integration/cat/cat.test.js
@@ -172,8 +172,8 @@ test('cross application tracing full integration', async function (t) {
       )
 
       // check the external segment for its properties
-      const externalSegment =
-        trace.root.children[0].children[trace.root.children[0].children.length - 1]
+      const [webSegment] = trace.getChildren(trace.root.id)
+      const [externalSegment] = trace.getChildren(webSegment.id)
       plan.equal(
         externalSegment.name.split('/')[0],
         'ExternalTransaction',
@@ -246,8 +246,8 @@ test('cross application tracing full integration', async function (t) {
       )
 
       // check the external segment for its properties
-      const externalSegment =
-        trace.root.children[0].children[trace.root.children[0].children.length - 1]
+      const [webSegment] = trace.getChildren(trace.root.id)
+      const [externalSegment] = trace.getChildren(webSegment.id)
       plan.equal(
         externalSegment.name.split('/')[0],
         'ExternalTransaction',
diff --git a/test/integration/core/crypto.test.js b/test/integration/core/crypto.test.js
index e9845463d6..4363338dc9 100644
--- a/test/integration/core/crypto.test.js
+++ b/test/integration/core/crypto.test.js
@@ -26,6 +26,7 @@ test('pbkdf2', function (t, end) {
     crypto.pbkdf2('hunter2', 'saltine', 5, 32, 'sha1', function (err, key) {
       assert.ok(!err, 'should not error')
       assert.equal(key.length, 32)
+      debugger
       verifySegments({ agent, end, name: 'crypto.pbkdf2' })
     })
   })
@@ -48,7 +49,8 @@ test('sync randomBytes', function (t, end) {
     const bytes = crypto.randomBytes(32)
     assert.ok(bytes instanceof Buffer)
     assert.equal(bytes.length, 32)
-    assert.equal(transaction.trace.root.children.length, 0)
+    const children = transaction.trace.getChildren(transaction.trace.root.id)
+    assert.equal(children.length, 0)
     end()
   })
 })
@@ -72,7 +74,8 @@ test('sync pseudoRandomBytes', function (t, end) {
     const bytes = crypto.pseudoRandomBytes(32)
     assert.ok(bytes instanceof Buffer)
     assert.equal(bytes.length, 32)
-    assert.equal(transaction.trace.root.children.length, 0)
+    const children = transaction.trace.getChildren(transaction.trace.root.id)
+    assert.equal(children.length, 0)
     end()
   })
 })
@@ -96,7 +99,8 @@ test('sync randomFill', function (t, end) {
     crypto.randomFillSync(buf)
     assert.ok(buf instanceof Buffer)
     assert.equal(buf.length, 10)
-    assert.equal(transaction.trace.root.children.length, 0)
+    const children = transaction.trace.getChildren(transaction.trace.root.id)
+    assert.equal(children.length, 0)
     end()
   })
 })
@@ -118,7 +122,8 @@ test('scryptSync', (t, end) => {
     const buf = crypto.scryptSync('secret', 'salt', 10)
     assert.ok(buf instanceof Buffer)
     assert.equal(buf.length, 10)
-    assert.equal(transaction.trace.root.children.length, 0)
+    const children = transaction.trace.getChildren(transaction.trace.root.id)
+    assert.equal(children.length, 0)
     end()
   })
 })
diff --git a/test/integration/core/fs.test.js b/test/integration/core/fs.test.js
index aae59dac02..b7a54bae72 100644
--- a/test/integration/core/fs.test.js
+++ b/test/integration/core/fs.test.js
@@ -852,7 +852,9 @@ test('read', async function (t) {
       plan.equal(len, 12, 'should read correct number of bytes')
       plan.equal(data.toString('utf8'), content)
       plan.equal(agent.getTransaction(), trans, 'should preserve transaction')
-      plan.equal(trans.trace.root.children.length, 0, 'should not create any segments')
+      const children = trans.trace.getChildren(trans.trace.root.id)
+      plan.equal(children.length, 0, 'should not create any segments')
+
     })
   })
 
@@ -875,7 +877,8 @@ test('write', async function (t) {
       plan.equal(len, 12, 'should write correct number of bytes')
       plan.equal(fs.readFileSync(name, 'utf8'), content)
       plan.equal(agent.getTransaction(), trans, 'should preserve transaction')
-      plan.equal(trans.trace.root.children.length, 0, 'should not create any segments')
+      const children = trans.trace.getChildren(trans.trace.root.id)
+      plan.equal(children.length, 0, 'should not create any segments')
     })
   })
 
@@ -898,7 +901,8 @@ test('watch (file)', async function (t) {
 
         plan.equal(file, 'watch-file', 'should have correct file name')
         plan.equal(agent.getTransaction(), trans, 'should preserve transaction')
-        plan.equal(trans.trace.root.children.length, 1, 'should not create any segments')
+        const children = trans.trace.getChildren(trans.trace.root.id)
+        plan.equal(children.length, 1, 'should not create any segments')
         watcher.close()
       })
       fs.writeFile(name, content + 'more', function (err) {
@@ -923,7 +927,8 @@ test('watch (dir)', async function (t) {
         plan.equal(ev, 'rename')
         plan.equal(file, 'watch-dir')
         plan.equal(agent.getTransaction(), trans, 'should preserve transaction')
-        plan.equal(trans.trace.root.children.length, 1, 'should not create any segments')
+        const children = trans.trace.getChildren(trans.trace.root.id)
+        plan.equal(children.length, 1, 'should not create any segments')
         watcher.close()
       })
       fs.writeFile(name, content, function (err) {
@@ -951,9 +956,9 @@ test('watch emitter', async function (t) {
         plan.equal(file, 'watch', 'should be for correct directory')
 
         const tx = agent.getTransaction()
-        const root = trans.trace.root
         plan.equal(tx && tx.id, trans.id, 'should preserve transaction')
-        plan.equal(root.children.length, 1, 'should not create any segments')
+        const children = trans.trace.getChildren(trans.trace.root.id)
+        plan.equal(children.length, 1, 'should not create any segments')
 
         watcher.close()
       })
@@ -985,7 +990,8 @@ test('watchFile', async function (t) {
         plan.ok(cur.size > prev.size, 'content modified')
 
         plan.equal(agent.getTransaction(), trans, 'should preserve transaction')
-        plan.equal(trans.trace.root.children.length, 0, 'should not create any segments')
+        const children = trans.trace.getChildren(trans.trace.root.id)
+        plan.equal(children.length, 0, 'should not create any segments')
         fs.unwatchFile(name, onChange)
       }
     })
diff --git a/test/integration/core/net.tap.js b/test/integration/core/net.tap.js
deleted file mode 100644
index b75da4afdc..0000000000
--- a/test/integration/core/net.tap.js
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright 2020 New Relic Corporation. All rights reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-'use strict'
-
-const test = require('tap').test
-const net = require('net')
-const helper = require('../../lib/agent_helper')
-
-function id(tx) {
-  return tx && tx.id
-}
-
-test('createServer', function createServerTest(t) {
-  const { agent, tracer } = setupAgent(t)
-
-  helper.runInTransaction(agent, function transactionWrapper(transaction) {
-    const server = net.createServer(handler)
-
-    server.listen(4123, function listening() {
-      const socket = net.connect({ port: 4123 })
-      socket.write('test123')
-      socket.end()
-    })
-
-    function handler(socket) {
-      t.equal(id(agent.getTransaction()), id(transaction), 'should maintain tx')
-      socket.end('test')
-      t.equal(
-        tracer.getSegment().name,
-        'net.Server.onconnection',
-        'child segment should have correct name'
-      )
-
-      socket.on('data', function onData(data) {
-        t.equal(id(agent.getTransaction()), id(transaction), 'should maintain tx')
-        t.equal(data.toString(), 'test123')
-        socket.end()
-        setTimeout(server.close.bind(server, onClose), 0)
-      })
-    }
-
-    function onClose() {
-      const root = agent.getTransaction().trace.root
-      t.equal(root.children.length, 2, 'should have a single child')
-      const child = root.children[1]
-      t.equal(child.name, 'net.Server.onconnection', 'child segment should have correct name')
-      t.ok(child.timer.touched, 'child should started and ended')
-      t.equal(child.children.length, 1, 'child should have a single child segment')
-      const timeout = child.children[0]
-      t.equal(timeout.name, 'timers.setTimeout', 'timeout segment should have correct name')
-      t.ok(timeout.timer.touched, 'timeout should started and ended')
-      t.equal(timeout.children.length, 1, 'timeout should have a single callback segment')
-      t.end()
-    }
-  })
-})
-
-test('connect', function connectTest(t) {
-  const { agent } = setupAgent(t)
-
-  const server = net.createServer(function connectionHandler(socket) {
-    socket.on('data', function onData(data) {
-      t.equal(data.toString(), 'some data')
-      socket.end('end data')
-    })
-  })
-
-  t.teardown(function () {
-    server.close()
-  })
-
-  server.listen(4123, function listening() {
-    helper.runInTransaction(agent, transactionWrapper)
-  })
-
-  function transactionWrapper(transaction) {
-    let count = 0
-    const socket = net.createConnection({ port: 4123 })
-    socket.on('data', function onData(data) {
-      t.equal(id(agent.getTransaction()), id(transaction), 'should maintain tx')
-      t.equal(data.toString(), 'end data')
-      ++count
-    })
-    socket.on('end', function onEnd() {
-      t.equal(id(agent.getTransaction()), id(transaction), 'should maintain tx')
-      t.equal(count, 1)
-      setTimeout(verify, 0)
-    })
-
-    socket.on('connect', function onConnet() {
-      t.equal(id(agent.getTransaction()), id(transaction), 'should maintain tx')
-      socket.write('some data')
-      socket.end()
-    })
-
-    function verify() {
-      if (!t.passing()) {
-        return t.end()
-      }
-
-      const root = agent.getTransaction().trace.root
-      t.equal(root.children.length, 1, 'should have a single child')
-      let connectSegment = root.children[0]
-      t.equal(
-        connectSegment.name,
-        'net.createConnection',
-        'connect segment should have correct name'
-      )
-      t.ok(connectSegment.timer.touched, 'connect should started and ended')
-
-      // Depending on the version of Node there may be another connection segment
-      // floating in the trace.
-      if (connectSegment.children[0].name === 'net.Socket.connect') {
-        connectSegment = connectSegment.children[0]
-      }
-
-      t.equal(connectSegment.children.length, 2, 'connect should have a two child segment')
-      const dnsSegment = connectSegment.children[0]
-      const timeoutSegment = connectSegment.children[1]
-
-      t.equal(dnsSegment.name, 'dns.lookup', 'dns segment should have correct name')
-      t.ok(dnsSegment.timer.touched, 'dns segment should started and ended')
-      t.equal(dnsSegment.children.length, 1, 'dns should have a single callback segment')
-      t.equal(timeoutSegment.name, 'timers.setTimeout', 'timeout segment should have correct name')
-      t.ok(timeoutSegment.timer.touched, 'timeout should started and ended')
-      t.equal(timeoutSegment.children.length, 1, 'timeout should have a single callback segment')
-      t.end()
-    }
-  }
-})
-
-test('createServer and connect', function createServerTest(t) {
-  const { agent, tracer } = setupAgent(t)
-
-  helper.runInTransaction(agent, function transactionWrapper(transaction) {
-    const server = net.createServer(handler)
-
-    server.listen(4123, function listening() {
-      const socket = net.connect({ port: 4123 })
-      socket.write('test123')
-      socket.end()
-    })
-
-    function handler(socket) {
-      t.equal(id(agent.getTransaction()), id(transaction), 'should maintain tx')
-      socket.end('test')
-      t.equal(
-        tracer.getSegment().name,
-        'net.Server.onconnection',
-        'child segment should have correct name'
-      )
-
-      socket.on('data', function onData(data) {
-        t.equal(id(agent.getTransaction()), id(transaction), 'should maintain tx')
-        t.equal(data.toString(), 'test123')
-        socket.end()
-        server.close(onClose)
-      })
-    }
-
-    function onClose() {
-      const root = agent.getTransaction().trace.root
-      t.equal(root.children.length, 2, 'should have 2 children')
-      let clientSegment = root.children[0]
-      t.equal(clientSegment.name, 'net.connect', 'server segment should have correct name')
-      t.ok(clientSegment.timer.touched, 'server should started and ended')
-
-      // Depending on the version of Node there may be another connection segment
-      // floating in the trace.
-      if (clientSegment.children[0].name === 'net.Socket.connect') {
-        clientSegment = clientSegment.children[0]
-      }
-
-      t.equal(clientSegment.children.length, 1, 'clientSegment should only have one child')
-      const dnsSegment = clientSegment.children[0]
-      if (dnsSegment) {
-        t.equal(dnsSegment.name, 'dns.lookup', 'dnsSegment is named properly')
-      } else {
-        t.fail('did not have children, prevent undefined property lookup')
-      }
-
-      const serverSegment = root.children[1]
-      t.equal(
-        serverSegment.name,
-        'net.Server.onconnection',
-        'server segment should have correct name'
-      )
-      t.ok(serverSegment.timer.touched, 'server should started and ended')
-      t.equal(serverSegment.children.length, 0, 'should not have any server segments')
-      t.end()
-    }
-  })
-})
-
-function setupAgent(t) {
-  const agent = helper.instrumentMockedAgent()
-  const tracer = helper.getTracer()
-
-  t.teardown(function tearDown() {
-    helper.unloadAgent(agent)
-  })
-
-  return {
-    agent,
-    tracer
-  }
-}
diff --git a/test/integration/core/net.test.js b/test/integration/core/net.test.js
index 33fd24cf7c..27eb99e25e 100644
--- a/test/integration/core/net.test.js
+++ b/test/integration/core/net.test.js
@@ -54,16 +54,18 @@ test('createServer', function createServerTest(t, end) {
     }
 
     function onClose() {
-      const root = agent.getTransaction().trace.root
-      assert.equal(root.children.length, 2, 'should have a single child')
-      const child = root.children[1]
+      const children = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(children.length, 2, 'should have a single child')
+      const child = children[1]
+      const childChildren = transaction.trace.getChildren(child.id)
       assert.equal(child.name, 'net.Server.onconnection', 'child segment should have correct name')
       assert.ok(child.timer.touched, 'child should started and ended')
-      assert.equal(child.children.length, 1, 'child should have a single child segment')
-      const timeout = child.children[0]
+      assert.equal(childChildren.length, 1, 'child should have a single child segment')
+      const timeout = childChildren[0]
+      const timeoutChildren = transaction.trace.getChildren(timeout.id)
       assert.equal(timeout.name, 'timers.setTimeout', 'timeout segment should have correct name')
       assert.ok(timeout.timer.touched, 'timeout should started and ended')
-      assert.equal(timeout.children.length, 1, 'timeout should have a single callback segment')
+      assert.equal(timeoutChildren.length, 1, 'timeout should have a single callback segment')
       end()
     }
   })
@@ -108,37 +110,41 @@ test('connect', function connectTest(t, end) {
     })
 
     function verify() {
-      const root = agent.getTransaction().trace.root
-      assert.equal(root.children.length, 1, 'should have a single child')
-      let connectSegment = root.children[0]
+      const transaction = agent.getTransaction()
+      const children = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(children.length, 1, 'should have a single child')
+      let connectSegment = children[0]
       assert.equal(
         connectSegment.name,
         'net.createConnection',
         'connect segment should have correct name'
       )
       assert.ok(connectSegment.timer.touched, 'connect should started and ended')
+      let connectChildren = transaction.trace.getChildren(connectSegment.id)
 
       // Depending on the version of Node there may be another connection segment
       // floating in the trace.
-      if (connectSegment.children[0].name === 'net.Socket.connect') {
-        connectSegment = connectSegment.children[0]
+      if (connectChildren[0].name === 'net.Socket.connect') {
+        connectSegment = connectChildren[0]
       }
+      connectChildren = transaction.trace.getChildren(connectSegment.id)
 
-      assert.equal(connectSegment.children.length, 2, 'connect should have a two child segment')
-      const dnsSegment = connectSegment.children[0]
-      const timeoutSegment = connectSegment.children[1]
+      assert.equal(connectChildren.length, 2, 'connect should have a two child segment')
+      const [dnsSegment, timeoutSegment] = connectChildren
 
       assert.equal(dnsSegment.name, 'dns.lookup', 'dns segment should have correct name')
       assert.ok(dnsSegment.timer.touched, 'dns segment should started and ended')
-      assert.equal(dnsSegment.children.length, 1, 'dns should have a single callback segment')
+      const dnsChildren = transaction.trace.getChildren(dnsSegment.id)
+      assert.equal(dnsChildren.length, 1, 'dns should have a single callback segment')
       assert.equal(
         timeoutSegment.name,
         'timers.setTimeout',
         'timeout segment should have correct name'
       )
       assert.ok(timeoutSegment.timer.touched, 'timeout should started and ended')
+      const timeoutChildren = transaction.trace.getChildren(timeoutSegment.id)
       assert.equal(
-        timeoutSegment.children.length,
+        timeoutChildren.length,
         1,
         'timeout should have a single callback segment'
       )
@@ -177,34 +183,38 @@ test('createServer and connect', function createServerTest(t, end) {
     }
 
     function onClose() {
-      const root = agent.getTransaction().trace.root
-      assert.equal(root.children.length, 2, 'should have 2 children')
-      let clientSegment = root.children[0]
+      const transaction = agent.getTransaction()
+      const children = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(children.length, 2, 'should have 2 children')
+      let clientSegment = children[0]
       assert.equal(clientSegment.name, 'net.connect', 'server segment should have correct name')
       assert.ok(clientSegment.timer.touched, 'server should started and ended')
+      let clientChildren = transaction.trace.getChildren(clientSegment.id)
 
       // Depending on the version of Node there may be another connection segment
       // floating in the trace.
-      if (clientSegment.children[0].name === 'net.Socket.connect') {
-        clientSegment = clientSegment.children[0]
+      if (clientChildren[0].name === 'net.Socket.connect') {
+        clientSegment = clientChildren[0]
       }
+      clientChildren = transaction.trace.getChildren(clientSegment.id)
 
-      assert.equal(clientSegment.children.length, 1, 'clientSegment should only have one child')
-      const dnsSegment = clientSegment.children[0]
+      assert.equal(clientChildren.length, 1, 'clientSegment should only have one child')
+      const [dnsSegment ] = clientChildren
       if (dnsSegment) {
         assert.equal(dnsSegment.name, 'dns.lookup', 'dnsSegment is named properly')
       } else {
         assert.ok(0, 'did not have children, prevent undefined property lookup')
       }
 
-      const serverSegment = root.children[1]
+      const serverSegment = children[1]
       assert.equal(
         serverSegment.name,
         'net.Server.onconnection',
         'server segment should have correct name'
       )
       assert.ok(serverSegment.timer.touched, 'server should started and ended')
-      assert.equal(serverSegment.children.length, 0, 'should not have any server segments')
+      const serverChildren = transaction.trace.getChildren(serverSegment.id)
+      assert.equal(serverChildren.length, 0, 'should not have any server segments')
       end()
     }
   })
diff --git a/test/integration/core/timers.test.js b/test/integration/core/timers.test.js
index 205378cb20..c85bc06ea5 100644
--- a/test/integration/core/timers.test.js
+++ b/test/integration/core/timers.test.js
@@ -36,10 +36,9 @@ test('setImmediate: segments', async function (t) {
   helper.runInTransaction(agent, function transactionWrapper(tx) {
     timers.setImmediate(function anonymous() {
       plan.equal(agent.getTransaction().id, tx.id, 'should be in expected transaction')
-      plan.ok(
-        !agent.getTransaction().trace.root.children.length,
-        'should not have any segment for setImmediate'
-      )
+      const transaction = agent.getTransaction()
+      const children = transaction.trace.getChildren(transaction.trace.root.id)
+      plan.equal(children.length, 0)
     })
   })
 
@@ -118,15 +117,13 @@ test('setImmediate: should not propagate segments for ended transaction', async
     helper.runInSegment(agent, 'test-segment', () => {
       const segment = agent.tracer.getSegment()
       plan.notEqual(segment.name, 'test-segment')
-      plan.equal(segment.children.length, 0, 'should not propagate segments when transaction ends')
+      const children = transaction.trace.getChildren(segment.id)
+      plan.equal(children.length, 0, 'should not propagate segments when transaction ends')
       setImmediate(() => {
         const segment = agent.tracer.getSegment()
         plan.notEqual(segment.name, 'test-segment')
-        plan.equal(
-          segment.children.length,
-          0,
-          'should not propagate segments when transaction ends'
-        )
+        const children = transaction.trace.getChildren(segment.id)
+        plan.equal(children.length, 0, 'should not propagate segments when transaction ends')
       })
     })
   })
@@ -158,7 +155,8 @@ test('global setImmediate', function testSetImmediate(t, end) {
   helper.runInTransaction(agent, function transactionWrapper(transaction) {
     setImmediate(function anonymous() {
       assert.equal(agent.getTransaction(), transaction)
-      assert.equal(agent.getTransaction().trace.root.children.length, 0)
+      const children = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(children.length, 0)
       end()
     })
   })
@@ -180,7 +178,8 @@ test('nextTick', async function testNextTick(t) {
   helper.runInTransaction(agent, function transactionWrapper(transaction) {
     process.nextTick(function callback() {
       plan.equal(agent.getTransaction(), transaction)
-      plan.equal(agent.getTransaction().trace.root.children.length, 0)
+      const children = transaction.trace.getChildren(transaction.trace.root.id)
+      plan.equal(children.length, 0)
     })
   })
 
@@ -196,7 +195,8 @@ test('nextTick with extra args', async function testNextTick(t) {
     process.nextTick(
       function callback() {
         plan.equal(agent.getTransaction(), transaction)
-        plan.equal(agent.getTransaction().trace.root.children.length, 0)
+        const children = transaction.trace.getChildren(transaction.trace.root.id)
+        plan.equal(children.length, 0)
         plan.deepEqual([].slice.call(arguments), [1, 2, 3])
         process.nextTick = original
       },
@@ -225,7 +225,8 @@ test('clearImmediate', (t, end) => {
   helper.runInTransaction(agent, function transactionWrapper(transaction) {
     process.nextTick(function callback() {
       const timer2 = setImmediate(assert.fail)
-      assert.ok(!transaction.trace.root.children[0])
+      const children = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(children.length, 0)
       clearImmediate(timer2)
       setImmediate(end)
     })
@@ -248,7 +249,7 @@ test('clearTimeout should ignore segment created for timer', async (t) => {
     process.nextTick(function callback() {
       const timer = setTimeout(plan.fail)
 
-      const timerSegment = transaction.trace.root.children[0]
+      const [timerSegment] = transaction.trace.getChildren(transaction.trace.root.id)
       plan.equal(timerSegment.name, 'timers.setTimeout')
       plan.equal(timerSegment.ignore, false)
 
@@ -272,7 +273,7 @@ test('clearTimeout should not ignore parent segment when opaque', async (t) => {
         segment.opaque = true
 
         const timer = setTimeout(plan.fail)
-        const parentSegment = transaction.trace.root.children[0]
+        const [parentSegment] = transaction.trace.getChildren(transaction.trace.root.id)
         plan.equal(parentSegment.name, expectedParentName)
         plan.equal(parentSegment.ignore, false)
 
@@ -298,7 +299,7 @@ test('clearTimeout should not ignore parent segment when internal', async (t) =>
 
         const timer = setTimeout(plan.fail)
 
-        const parentSegment = transaction.trace.root.children[0]
+        const [parentSegment] = transaction.trace.getChildren(transaction.trace.root.id)
         plan.equal(parentSegment.name, expectedParentName)
         plan.equal(parentSegment.ignore, false)
 
diff --git a/test/integration/core/verify.js b/test/integration/core/verify.js
index f2f0a1ea0a..bfcb5c1856 100644
--- a/test/integration/core/verify.js
+++ b/test/integration/core/verify.js
@@ -8,22 +8,24 @@
 module.exports = verifySegments
 
 function verifySegments({ agent, name, children = [], end, assert = require('node:assert') }) {
-  const root = agent.getTransaction().trace.root
-  assert.equal(root.children.length, 1, 'should have a single child')
-  const child = root.children[0]
+  const { trace } = agent.getTransaction()
+  const traceChildren = trace.getChildren(trace.root.id)
+  assert.equal(traceChildren.length, 1, 'should have a single child')
+  const child = traceChildren[0]
+  const childChildren = trace.getChildren(child.id)
   assert.equal(child.name, name, 'child segment should have correct name')
   assert.ok(child.timer.touched, 'child should started and ended')
   assert.equal(
-    child.children.length,
+    childChildren.length,
     1 + children.length,
     'child should have a single callback segment'
   )
 
   for (let i = 0; i < children.length; ++i) {
-    assert.equal(child.children[i].name, children[i])
+    assert.equal(childChildren[i].name, children[i])
   }
 
-  const callback = child.children[child.children.length - 1]
+  const callback = childChildren[childChildren.length - 1]
   assert.ok(
     callback.name === 'Callback: anonymous' || callback.name === 'Callback: <anonymous>',
     'callback segment should have correct name'
diff --git a/test/integration/instrumentation/fetch.test.js b/test/integration/instrumentation/fetch.test.js
index 4a9536d114..b17706a625 100644
--- a/test/integration/instrumentation/fetch.test.js
+++ b/test/integration/instrumentation/fetch.test.js
@@ -79,7 +79,7 @@ test('fetch', async function (t) {
       })
       assert.equal(status, 200)
 
-      assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false })
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/post`], { exact: false })
       tx.end()
     })
   })
@@ -88,7 +88,7 @@ test('fetch', async function (t) {
     await helper.runInTransaction(agent, async (tx) => {
       const { status } = await fetch(`${REQUEST_URL}/get?a=b&c=d`)
       assert.equal(status, 200)
-      const segment = metrics.findSegment(tx.trace.root, `External/${HOST}/get`)
+      const segment = metrics.findSegment(tx.trace, tx.trace.root, `External/${HOST}/get`)
       const attrs = segment.getAttributes()
       assert.equal(attrs.url, `${REQUEST_URL}/get`)
       assert.equal(attrs.procedure, 'GET')
@@ -144,7 +144,7 @@ test('fetch', async function (t) {
       const [{ status }, { status: status2 }] = await Promise.all([req1, req2])
       assert.equal(status, 200)
       assert.equal(status2, 200)
-      assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], {
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], {
         exact: false
       })
       tx.end()
@@ -159,7 +159,7 @@ test('fetch', async function (t) {
         })
       } catch (err) {
         assert.equal(err.message, 'fetch failed')
-        assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false })
+        assertSegments(tx.trace, tx.trace.root, ['External/invalidurl/foo'], { exact: false })
         assert.equal(tx.exceptions.length, 1)
         tx.end()
       }
@@ -178,7 +178,7 @@ test('fetch', async function (t) {
         }, 100)
         await req
       } catch (err) {
-        assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false })
+        assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false })
         assert.equal(tx.exceptions.length, 1)
         assert.equal(tx.exceptions[0].error.name, 'AbortError')
         tx.end()
@@ -204,11 +204,11 @@ test('fetch', async function (t) {
       try {
         await req
       } catch (error) {
-        assertSegments(transaction.trace.root, [`External/localhost:${port}/`], {
+        assertSegments(transaction.trace, transaction.trace.root, [`External/localhost:${port}/`], {
           exact: false
         })
 
-        const segments = transaction.trace.root.children
+        const segments = transaction.trace.getChildren(transaction.trace.root.id)
         const segment = segments[segments.length - 1]
 
         assert.ok(segment.timer.start, 'should have started')
@@ -223,7 +223,7 @@ test('fetch', async function (t) {
     await helper.runInTransaction(agent, async (tx) => {
       const { status } = await fetch(`${REQUEST_URL}/status/400`)
       assert.equal(status, 400)
-      assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false })
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/status/400`], { exact: false })
       tx.end()
     })
   })
diff --git a/test/integration/instrumentation/http-outbound.test.js b/test/integration/instrumentation/http-outbound.test.js
index 594cdb225a..8c3bf509a2 100644
--- a/test/integration/instrumentation/http-outbound.test.js
+++ b/test/integration/instrumentation/http-outbound.test.js
@@ -36,11 +36,11 @@ test('external requests', async function (t) {
 
     notVeryReliable.listen(0)
 
-    helper.runInTransaction(agent, function inTransaction() {
+    helper.runInTransaction(agent, function inTransaction(tx) {
       const req = http.get(notVeryReliable.address())
 
       req.on('error', function onError() {
-        const segment = agent.tracer.getTransaction().trace.root.children[0]
+        const [segment] = tx.trace.getChildren(tx.trace.root.id)
 
         assert.equal(
           segment.name,
@@ -86,7 +86,7 @@ test('external requests', async function (t) {
     })
 
     function check(tx) {
-      const external = tx.trace.root.children[0]
+      const [external] = tx.trace.getChildren(tx.trace.root.id)
       assert.equal(
         external.name,
         'External/localhost:' + server.address().port + '/some/path',
@@ -94,21 +94,24 @@ test('external requests', async function (t) {
       )
       assert.ok(external.timer.start, 'should have started')
       assert.ok(external.timer.hasEnd(), 'should have ended')
-      assert.ok(external.children.length, 'should have children')
+      const externalChildren = tx.trace.getChildren(external.id)
+      assert.ok(externalChildren.length, 'should have children')
 
-      let connect = external.children[0]
+      let connect = externalChildren[0]
       assert.equal(connect.name, 'http.Agent#createConnection', 'should be connect segment')
-      assert.equal(connect.children.length, 1, 'connect should have 1 child')
+      let connectChildren = tx.trace.getChildren(connect.id)
+      assert.equal(connectChildren.length, 1, 'connect should have 1 child')
 
       // There is potentially an extra layer of create/connect segments.
-      if (connect.children[0].name === 'net.Socket.connect') {
-        connect = connect.children[0]
+      if (connectChildren[0].name === 'net.Socket.connect') {
+        connect = connectChildren[0]
       }
+      connectChildren = tx.trace.getChildren(connect.id)
 
-      const dnsLookup = connect.children[0]
+      const dnsLookup = connectChildren[0]
       assert.equal(dnsLookup.name, 'dns.lookup', 'should be dns.lookup segment')
 
-      const callback = external.children[external.children.length - 1]
+      const callback = externalChildren[externalChildren.length - 1]
       assert.equal(callback.name, 'timers.setTimeout', 'should have timeout segment')
 
       end()
@@ -138,7 +141,8 @@ test('external requests', async function (t) {
       const req = http.get(opts, function onResponse(res) {
         res.resume()
         res.once('end', function () {
-          const segment = agent.tracer.getTransaction().trace.root.children[0]
+          const { trace } = agent.tracer.getTransaction()
+          const [segment] = trace.getChildren(trace.root.id)
           assert.equal(
             segment.name,
             `External/www.google.com/proxy/path`,
@@ -167,15 +171,16 @@ test('external requests', async function (t) {
     })
 
     function check() {
-      const root = agent.tracer.getTransaction().trace.root
-      const segment = root.children[0]
+      const tx = agent.getTransaction()
+      const [segment] = tx.trace.getChildren(tx.trace.root.id)
 
       assert.equal(segment.name, 'External/example.com/', 'should be named')
       assert.ok(segment.timer.start, 'should have started')
       assert.ok(segment.timer.hasEnd(), 'should have ended')
-      assert.equal(segment.children.length, 1, 'should have 1 child')
+      const segmentChildren = tx.trace.getChildren(segment.id)
+      assert.equal(segmentChildren.length, 1, 'should have 1 child')
 
-      const notDuped = segment.children[0]
+      const notDuped = segmentChildren[0]
       assert.notEqual(
         notDuped.name,
         segment.name,
@@ -214,7 +219,7 @@ test('external requests', async function (t) {
       http.get('http://example.com', (res) => {
         res.resume()
         res.on('end', () => {
-          const segment = tx.trace.root.children[0]
+          const [segment] = tx.trace.getChildren(tx.trace.root.id)
           assert.equal(segment.name, 'External/example.com/', 'should create external segment')
           end()
         })
@@ -229,7 +234,7 @@ test('external requests', async function (t) {
       const req = http.get('http://example.com', (res) => {
         res.resume()
         res.on('end', () => {
-          const segment = tx.trace.root.children[0]
+          const [segment] = tx.trace.getChildren(tx.trace.root.id)
           const attrs = segment.getAttributes()
           assert.deepEqual(attrs, {
             url: 'http://example.com/',
diff --git a/test/integration/transaction/tracer.test.js b/test/integration/transaction/tracer.test.js
index 5aeb19f0d2..e9d0b9d43f 100644
--- a/test/integration/transaction/tracer.test.js
+++ b/test/integration/transaction/tracer.test.js
@@ -280,9 +280,12 @@ test('getSegment', async function testGetTransaction(t) {
     plan.equal(tracer.getSegment(), root)
 
     setTimeout(function onTimeout() {
-      const segment = root.children[0].children[0]
-      plan.equal(tracer.getSegment(), segment)
-      plan.equal(tracer.getSegment().name, 'Callback: onTimeout')
+      const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+      const [setTimeoutCb] = transaction.trace.getChildren(child.id)
+      const segment = tracer.getSegment()
+
+      plan.equal(segment, setTimeoutCb)
+      plan.equal(segment.name, 'Callback: onTimeout')
     }, 0)
   })
 
@@ -371,12 +374,15 @@ test('addSegment', async function addSegmentTest(t) {
 
     plan.equal(segment.name, 'inside')
     root = transaction.trace.root
-    plan.equal(root.children[0], segment)
+    let [child] = transaction.trace.getChildren(root.id)
+    plan.equal(child, segment)
+
 
     const outside = tracer.addSegment('outside', null, root, false, check)
+    ;[, child] = transaction.trace.getChildren(root.id)
 
     plan.equal(outside.name, 'outside')
-    plan.equal(root.children[1], outside)
+    plan.equal(child, outside)
   })
 
   function check(segment) {
@@ -399,7 +405,9 @@ test('addSegment + recorder', async function addSegmentTest(t) {
 
     plan.equal(segment.name, 'inside')
     plan.equal(segment.timer.hrDuration, null)
-    plan.equal(root.children[0], segment)
+    const [child] = transaction.trace.getChildren(root.id)
+    plan.equal(child, segment)
+
     transaction.end()
   })
 
@@ -429,7 +437,9 @@ test('addSegment + full', async function addSegmentTest(t) {
 
     plan.equal(segment.name, 'inside')
     plan.ok(segment.timer.hrDuration)
-    plan.equal(root.children[0], segment)
+    const [child] = transaction.trace.getChildren(root.id)
+    plan.equal(child, segment)
+
     transaction.end()
   })
 
@@ -612,12 +622,14 @@ test('wrapFunction', async function testwrapFunction(t) {
   function makeCallback(val) {
     return function callback(parent, arg) {
       const segment = tracer.getSegment()
+      const transaction = tracer.getTransaction()
       plan.equal(arg, val)
       plan.equal(this, inner)
       if (parent) {
+        const children = transaction.trace.getChildren(parent.id)
         plan.ok(segment.timer.hrstart)
         plan.ok(!segment.timer.hrDuration)
-        plan.notEqual(parent.children.indexOf(segment), -1)
+        plan.notEqual(children.indexOf(segment), -1)
       }
 
       return val
@@ -638,8 +650,10 @@ test('wrapFunction', async function testwrapFunction(t) {
 
     plan.equal(this, outer)
     process.nextTick(function next() {
+      let children
       if (segment) {
-        plan.equal(segment.children.length, 0)
+        children = transaction.trace.getChildren(segment.id)
+        plan.equal(children.length, 0)
       }
 
       plan.equal(a.call(inner, segment, 'a'), 'a')
@@ -647,7 +661,8 @@ test('wrapFunction', async function testwrapFunction(t) {
       plan.equal(c.call(inner, segment, 'c'), 'c')
 
       if (segment) {
-        segment.children.forEach(function (child) {
+        children = transaction.trace.getChildren(segment.id)
+        children.forEach(function (child) {
           plan.ok(child.timer.hrstart)
           plan.ok(child.timer.hrDuration)
         })
@@ -706,13 +721,15 @@ test('wrapFunctionLast', async function testwrapFunctionLast(t) {
 
   function callback(parent, callbackArgs) {
     const segment = tracer.getSegment()
+    const transaction = tracer.getTransaction()
     plan.deepEqual(callbackArgs, [1, 2, 3])
     plan.equal(this, inner)
 
     if (parent) {
       plan.ok(segment.timer.hrstart)
       plan.ok(!segment.timer.hrDuration)
-      plan.equal(parent.children[0], segment)
+      const [child] = transaction.trace.getChildren(parent.id)
+      plan.equal(child, segment)
     }
 
     return innerReturn
@@ -734,16 +751,19 @@ test('wrapFunctionLast', async function testwrapFunctionLast(t) {
 
     plan.equal(this, outer)
     process.nextTick(function next() {
+      let children
       if (segment) {
-        plan.equal(segment.children.length, 0)
+        children = transaction.trace.getChildren(segment.id)
+        plan.equal(children.length, 0)
       }
 
       plan.equal(cb.call(inner, segment, cbArgs), innerReturn)
 
       if (segment) {
-        plan.equal(segment.children.length, 1)
-        plan.ok(segment.children[0].timer.hrstart)
-        plan.ok(segment.children[0].timer.hrDuration)
+        children = transaction.trace.getChildren(segment.id)
+        plan.equal(children.length, 1)
+        plan.ok(children[0].timer.hrstart)
+        plan.ok(children[0].timer.hrDuration)
         plan.ok(segment.timer.hrDuration)
         transaction.end()
       }
@@ -781,13 +801,15 @@ test('wrapFunctionFirst', async function testwrapFunctionFirst(t) {
 
   function callback(parent, args) {
     const segment = tracer.getSegment()
+    const transaction = tracer.getTransaction()
     plan.deepEqual(args, [1, 2, 3])
     plan.equal(this, inner)
 
     if (parent) {
       plan.ok(segment.timer.hrstart)
       plan.ok(!segment.timer.hrDuration)
-      plan.equal(parent.children[0], segment)
+      const [child] = transaction.trace.getChildren(parent.id)
+      plan.equal(child, segment)
     }
 
     return innerReturn
@@ -808,16 +830,19 @@ test('wrapFunctionFirst', async function testwrapFunctionFirst(t) {
 
     plan.equal(this, outer)
     process.nextTick(function next() {
+      let children
       if (segment) {
-        plan.equal(segment.children.length, 0)
+        children = transaction.trace.getChildren(segment.id)
+        plan.equal(children.length, 0)
       }
 
       plan.equal(cb.call(inner, segment, args), innerReturn)
 
       if (segment) {
-        plan.equal(segment.children.length, 1)
-        plan.ok(segment.children[0].timer.hrstart)
-        plan.ok(segment.children[0].timer.hrDuration)
+        children = transaction.trace.getChildren(segment.id)
+        plan.equal(children.length, 1)
+        plan.ok(children[0].timer.hrstart)
+        plan.ok(children[0].timer.hrDuration)
         plan.ok(segment.timer.hrDuration)
         transaction.end()
       }
@@ -843,8 +868,9 @@ test('wrapSyncFunction', async function testwrapSyncFunction(t) {
 
   helper.runInTransaction(agent, function inTrans(transaction) {
     wrapped(transaction, [4], 4)
-    plan.ok(transaction.trace.root.children[0].timer.hrstart)
-    plan.ok(transaction.trace.root.children[0].timer.hrDuration)
+    const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+    plan.ok(child.timer.hrstart)
+    plan.ok(child.timer.hrDuration)
     transaction.end()
   })
 
@@ -859,7 +885,8 @@ test('wrapSyncFunction', async function testwrapSyncFunction(t) {
   }
 
   function record(segment, scope, transaction) {
-    plan.equal(segment, transaction.trace.root.children[0])
+    const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+    plan.equal(segment, child)
     plan.equal(segment.name, 'my segment')
   }
 })
diff --git a/test/lib/agent_helper.js b/test/lib/agent_helper.js
index 45ab23947b..73e4ad1c4e 100644
--- a/test/lib/agent_helper.js
+++ b/test/lib/agent_helper.js
@@ -83,14 +83,6 @@ helper.loadMockedAgent = function loadMockedAgent(conf, setState = true) {
   // agent needs a 'real' configuration
   const configurator = require('../../lib/config')
   const config = configurator.createInstance(conf)
-
-  if (!config.debug) {
-    config.debug = {}
-  }
-
-  // adds link to parents node in traces for easier testing
-  config.debug.double_linked_transactions = true
-
   // stub applications
   config.applications = () => ['New Relic for Node.js tests']
 
diff --git a/test/lib/custom-assertions/assert-segments.js b/test/lib/custom-assertions/assert-segments.js
index a56d57257c..6111b2a3a5 100644
--- a/test/lib/custom-assertions/assert-segments.js
+++ b/test/lib/custom-assertions/assert-segments.js
@@ -63,6 +63,7 @@
  * followed by an array of strings indicates that the first string is a parent
  * element, and the subsequent array of strings is its child elements.
  *
+ * @param {Trace} trace             Transaction trace
  * @param {TraceSegment} parent     Parent segment
  * @param {Array} expected          Array of strings that represent segment names.
  *                                  If an item in the array is another array, it
@@ -79,6 +80,7 @@
  * @param {object} [deps.assert] Assertion library to use.
  */
 module.exports = function assertSegments(
+  trace,
   parent,
   expected,
   options,
@@ -96,7 +98,8 @@ module.exports = function assertSegments(
   }
 
   function getChildren(_parent) {
-    return _parent.children.filter(function (item) {
+    const children = trace.getChildren(_parent.id)
+    return children.filter(function (item) {
       if (exact && options && options.exclude) {
         return options.exclude.indexOf(item.name) === -1
       }
@@ -131,7 +134,7 @@ module.exports = function assertSegments(
           )
         }
       } else if (typeof sequenceItem === 'object') {
-        assertSegments(child, sequenceItem, options, { assert })
+        assertSegments(trace, child, sequenceItem, options, { assert })
       }
     }
 
@@ -143,14 +146,14 @@ module.exports = function assertSegments(
 
       if (typeof sequenceItem === 'string') {
         // find corresponding child in parent
-        for (let j = 0; j < parent.children.length; j++) {
-          if (parent.children[j].name === sequenceItem) {
-            child = parent.children[j]
+        for (let j = 0; j < children.length; j++) {
+          if (children[j].name === sequenceItem) {
+            child = children[j]
           }
         }
         assert.ok(child, 'segment "' + parent.name + '" should have child "' + sequenceItem + '"')
         if (typeof expected[i + 1] === 'object') {
-          assertSegments(child, expected[i + 1], { exact }, { assert })
+          assertSegments(trace, child, expected[i + 1], { exact }, { assert })
         }
       }
     }
diff --git a/test/lib/custom-assertions/compare-segments.js b/test/lib/custom-assertions/compare-segments.js
index fde765b060..690707de37 100644
--- a/test/lib/custom-assertions/compare-segments.js
+++ b/test/lib/custom-assertions/compare-segments.js
@@ -9,18 +9,21 @@
  * Verifies the expected length of children segments and that every
  * id matches between a segment array and the children
  *
- * @param {Object} parent trace
- * @param {Array} segments list of expected segments
- * @param {object} [deps] Injected dependencies.
- * @param {object} [deps.assert] Assertion library to use.
+ * @param {object} params to function
+ * @param {TraceSegment} params.parent segment
+ * @param {Array} params.segments list of expected segments
+ * @param {Trace} params.trace transaction trace
+ * @param {object} [params.assert] Assertion library to use.
  */
-module.exports = function compareSegments(
+module.exports = function compareSegments({
   parent,
   segments,
-  { assert = require('node:assert') } = {}
-) {
-  assert.ok(parent.children.length, segments.length, 'should be the same amount of children')
+  trace,
+  assert = require('node:assert')
+}) {
+  const parentChildren = trace.getChildren(parent.id)
+  assert.ok(parentChildren.length, segments.length, 'should be the same amount of children')
   segments.forEach((segment, index) => {
-    assert.equal(parent.children[index].id, segment.id, 'should have same ids')
+    assert.equal(parentChildren[index].id, segment.id, 'should have same ids')
   })
 }
diff --git a/test/lib/metrics_helper.js b/test/lib/metrics_helper.js
index 9feba35598..1853dc0d16 100644
--- a/test/lib/metrics_helper.js
+++ b/test/lib/metrics_helper.js
@@ -10,13 +10,14 @@ const urltils = require('../../lib/util/urltils')
 exports.findSegment = findSegment
 exports.getMetricHostName = getMetricHostName
 
-function findSegment(root, name) {
+function findSegment(trace, root, name) {
+  const children = trace.getChildren(root.id)
   if (root.name === name) {
     return root
-  } else if (root.children && root.children.length) {
-    for (let i = 0; i < root.children.length; i++) {
-      const child = root.children[i]
-      const found = findSegment(child, name)
+  } else if (children.length) {
+    for (let i = 0; i < children.length; i++) {
+      const child = children[i]
+      const found = findSegment(trace, child, name)
       if (found) {
         return found
       }
diff --git a/test/smoke/client-s3.test.js b/test/smoke/client-s3.test.js
index 1e0944d6c3..a0465ec10e 100644
--- a/test/smoke/client-s3.test.js
+++ b/test/smoke/client-s3.test.js
@@ -41,8 +41,8 @@ test('@aws-sdk/client-s3 functionality', async (t) => {
 
     transaction.end()
 
-    const { url, procedure, ...awsAttributes } =
-      transaction.trace.root.children[1].attributes.get(TRANS_SEGMENT)
+    const [, child] = transaction.trace.getChildren(transaction.trace.root.id)
+    const { url, procedure, ...awsAttributes } = child.attributes.get(TRANS_SEGMENT)
 
     delete awsAttributes.nr_exclusive_duration_millis
 
diff --git a/test/unit/agent/agent.test.js b/test/unit/agent/agent.test.js
index 4dad9bd146..cb1cfe8be9 100644
--- a/test/unit/agent/agent.test.js
+++ b/test/unit/agent/agent.test.js
@@ -101,11 +101,6 @@ test('when loaded with defaults', async (t) => {
     const { agent } = t.nr
     assert.throws(() => agent.setState('bogus'), /Invalid state bogus/)
   })
-
-  await t.test('has some debugging configuration by default', (t) => {
-    const { agent } = t.nr
-    assert.equal(Object.hasOwn(agent.config, 'debug'), true)
-  })
 })
 
 test('should load naming rules when configured', () => {
diff --git a/test/unit/api/api-start-background-transaction.test.js b/test/unit/api/api-start-background-transaction.test.js
index bbbfda97d5..2479e4fc1e 100644
--- a/test/unit/api/api-start-background-transaction.test.js
+++ b/test/unit/api/api-start-background-transaction.test.js
@@ -51,7 +51,7 @@ test('Agent API - startBackgroundTransaction', async (t) => {
       assert.ok(transaction.isActive())
 
       const currentSegment = tracer.getSegment()
-      const nestedSegment = currentSegment.children[0]
+      const [nestedSegment] = transaction.trace.getChildren(currentSegment.id)
       assert.equal(nestedSegment.name, 'Nodejs/nested')
     })
 
@@ -182,6 +182,31 @@ test('Agent API - startBackgroundTransaction', async (t) => {
     })
   })
 
+  await t.test('should record metrics', (t, end) => {
+    const { agent, api } = t.nr
+    let transaction
+    api.startBackgroundTransaction('test', function () {
+      transaction = agent.tracer.getTransaction()
+    })
+
+    transaction.end()
+    const metrics = transaction.metrics.unscoped
+    ;[
+      'OtherTransaction/Nodejs/test',
+      'OtherTransactionTotalTime/Nodejs/test',
+      'OtherTransaction/all',
+      'OtherTransactionTotalTime',
+      'OtherTransactionTotalTime',
+      'DurationByCaller/Unknown/Unknown/Unknown/Unknown/all',
+      'DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther'
+    ].forEach((metric) => {
+      assert.ok(metrics[metric].total, `${metric} has total`)
+      assert.ok(metrics[metric].totalExclusive, `${metric} has totalExclusive`)
+    })
+
+    end()
+  })
+
   await t.test('should not throw when no handler is supplied', (t, end) => {
     const { api } = t.nr
     assert.doesNotThrow(() => api.startBackgroundTransaction('test'))
@@ -221,7 +246,8 @@ test('Agent API - startBackgroundTransaction', async (t) => {
           api.startBackgroundTransaction('nested-clm-test', function () {
             nested({ api })
             const currentSegment = tracer.getSegment()
-            const nestedSegment = currentSegment.children[0]
+            const transaction = agent.tracer.getTransaction()
+            const [nestedSegment] = transaction.trace.getChildren(currentSegment.id)
             assertCLMAttrs({
               segments: [
                 {
diff --git a/test/unit/api/api-start-segment.test.js b/test/unit/api/api-start-segment.test.js
index b8efac74b4..503ccf8300 100644
--- a/test/unit/api/api-start-segment.test.js
+++ b/test/unit/api/api-start-segment.test.js
@@ -88,9 +88,13 @@ test('Agent API - startSegment', async (t) => {
 
       const transactionScopedCustomMetric = transactionNameMetric['Custom/foobar']
       assert.ok(transactionScopedCustomMetric)
+      assert.ok(transactionScopedCustomMetric.total)
+      assert.ok(transactionScopedCustomMetric.totalExclusive)
 
       const unscopedCustomMetric = tx.metrics.unscoped['Custom/foobar']
       assert.ok(unscopedCustomMetric)
+      assert.ok(unscopedCustomMetric.total)
+      assert.ok(unscopedCustomMetric.totalExclusive)
 
       end()
     })
diff --git a/test/unit/api/api-start-web-transaction.test.js b/test/unit/api/api-start-web-transaction.test.js
index 95d1dc839c..7eb39274fa 100644
--- a/test/unit/api/api-start-web-transaction.test.js
+++ b/test/unit/api/api-start-web-transaction.test.js
@@ -49,7 +49,7 @@ test('Agent API - startWebTransaction', async (t) => {
       assert.ok(transaction.isActive())
 
       const currentSegment = tracer.getSegment()
-      const nestedSegment = currentSegment.children[0]
+      const [nestedSegment] = transaction.trace.getChildren(currentSegment.id)
       assert.equal(nestedSegment.name, 'nested')
     })
 
@@ -174,9 +174,10 @@ test('Agent API - startWebTransaction', async (t) => {
           const { agent, api, tracer } = t.nr
           agent.config.code_level_metrics.enabled = enabled
           api.startWebTransaction('clm-nested-test', function () {
+            const tx = agent.tracer.getTransaction()
             nested({ api })
             const currentSegment = tracer.getSegment()
-            const nestedSegment = currentSegment.children[0]
+            const [nestedSegment] = tx.trace.getChildren(currentSegment.id)
             assertCLMAttrs({
               segments: [
                 {
diff --git a/test/unit/instrumentation/core/promises.test.js b/test/unit/instrumentation/core/promises.test.js
index 73074d322b..89cc6fad25 100644
--- a/test/unit/instrumentation/core/promises.test.js
+++ b/test/unit/instrumentation/core/promises.test.js
@@ -158,7 +158,7 @@ function checkTrace(t, tx) {
   const expectedSegment = tracer.getSegment()
   const segment = tx.trace.root
   assert.equal(segment.name, 'a')
-  assert.equal(segment.children.length, 0)
+  assert.equal(tx.trace.getChildren(segment.id).length, 0)
   // verify current segment is same as trace root
   assert.deepEqual(segment.name, expectedSegment.name, 'current segment is same as one in tracer')
   return Promise.resolve()
diff --git a/test/unit/instrumentation/http/outbound.test.js b/test/unit/instrumentation/http/outbound.test.js
index 508b0daba6..b0f0b96b21 100644
--- a/test/unit/instrumentation/http/outbound.test.js
+++ b/test/unit/instrumentation/http/outbound.test.js
@@ -49,7 +49,8 @@ test('instrumentOutbound', async (t) => {
       const req = new events.EventEmitter()
       helper.runInTransaction(agent, function (transaction) {
         instrumentOutbound(agent, { host: HOSTNAME, port: PORT }, makeFakeRequest)
-        assert.deepEqual(transaction.trace.root.children[0].getAttributes(), {})
+        const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+        assert.deepEqual(child.getAttributes(), {})
 
         function makeFakeRequest() {
           req.path = '/asdf?a=b&another=yourself&thing&grownup=true'
@@ -66,7 +67,8 @@ test('instrumentOutbound', async (t) => {
     const req = new events.EventEmitter()
     helper.runInTransaction(agent, function (transaction) {
       instrumentOutbound(agent, { host: HOSTNAME, port: PORT }, makeFakeRequest)
-      assert.deepEqual(transaction.trace.root.children[0].getAttributes(), {
+      const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.deepEqual(child.getAttributes(), {
         procedure: 'GET',
         url: `http://${HOSTNAME}:${PORT}/asdf`
       })
@@ -91,7 +93,8 @@ test('instrumentOutbound', async (t) => {
     const req = new events.EventEmitter()
     helper.runInTransaction(agent, function (transaction) {
       instrumentOutbound(agent, { host: HOSTNAME, port: PORT }, makeFakeRequest)
-      assert.deepEqual(transaction.trace.root.children[0].getAttributes(), {
+      const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.deepEqual(child.getAttributes(), {
         procedure: 'GET',
         url: `http://${HOSTNAME}:${PORT}/***`
       })
@@ -112,7 +115,8 @@ test('instrumentOutbound', async (t) => {
       const name = NAMES.EXTERNAL.PREFIX + HOSTNAME + ':' + PORT + path
 
       instrumentOutbound(agent, { host: HOSTNAME, port: PORT }, makeFakeRequest)
-      assert.equal(transaction.trace.root.children[0].name, name)
+      const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(child.name, name)
 
       function makeFakeRequest() {
         req.path = '/asdf?a=b&another=yourself&thing&grownup=true'
@@ -128,8 +132,9 @@ test('instrumentOutbound', async (t) => {
     helper.runInTransaction(agent, function (transaction) {
       agent.config.attributes.enabled = true
       instrumentOutbound(agent, { host: HOSTNAME, port: PORT }, makeFakeRequest)
+      const [child] = transaction.trace.getChildren(transaction.trace.root.id)
       assert.deepEqual(
-        transaction.trace.root.children[0].attributes.get(DESTINATIONS.SPAN_EVENT),
+        child.attributes.get(DESTINATIONS.SPAN_EVENT),
         {
           'hostname': HOSTNAME,
           'port': PORT,
@@ -175,7 +180,8 @@ test('instrumentOutbound', async (t) => {
       const name = NAMES.EXTERNAL.PREFIX + HOSTNAME + ':' + PORT + path
       req.path = path
       instrumentOutbound(agent, { host: HOSTNAME, port: PORT }, makeFakeRequest)
-      assert.equal(transaction.trace.root.children[0].name, name)
+      const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(child.name, name)
       end()
     })
 
@@ -193,7 +199,8 @@ test('instrumentOutbound', async (t) => {
       const name = NAMES.EXTERNAL.PREFIX + HOSTNAME + ':' + PORT + '/newrelic'
       req.path = path
       instrumentOutbound(agent, { host: HOSTNAME, port: PORT }, makeFakeRequest)
-      assert.equal(transaction.trace.root.children[0].name, name)
+      const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+      assert.equal(child.name, name)
     })
 
     function makeFakeRequest() {
@@ -473,7 +480,8 @@ test('when working with http.request', async (t) => {
       opts.method = 'POST'
 
       const req = http.request(opts, function (res) {
-        const attributes = transaction.trace.root.children[0].getAttributes()
+        const [child] = transaction.trace.getChildren(transaction.trace.root.id)
+        const attributes = child.getAttributes()
         assert.equal(attributes.url, 'http://www.google.com/index.html')
         assert.equal(attributes.procedure, 'POST')
         res.resume()
diff --git a/test/unit/instrumentation/prisma-client.test.js b/test/unit/instrumentation/prisma-client.test.js
index 4101d0e06f..dd299cbecf 100644
--- a/test/unit/instrumentation/prisma-client.test.js
+++ b/test/unit/instrumentation/prisma-client.test.js
@@ -127,7 +127,7 @@ test('PrismaClient unit.tests', async (t) => {
         args: { query: 'select test from schema.unit-test;' },
         action: 'executeRaw'
       })
-      const { children } = tx.trace.root
+      const children = tx.trace.getChildren(tx.trace.root.id)
       assert.equal(children.length, 3, 'should have 3 segments')
       const [firstSegment, secondSegment, thirdSegment] = children
       assert.equal(firstSegment.name, 'Datastore/statement/Prisma/user/create')
@@ -180,7 +180,7 @@ test('PrismaClient unit.tests', async (t) => {
 
     helper.runInTransaction(agent, async (tx) => {
       await client._executeRequest({ action: 'executeRaw' })
-      const { children } = tx.trace.root
+      const children = tx.trace.getChildren(tx.trace.root.id)
       const [firstSegment] = children
       assert.equal(firstSegment.name, 'Datastore/statement/Prisma/other/other')
       end()
@@ -228,7 +228,7 @@ test('PrismaClient unit.tests', async (t) => {
         args: [['select test from unit-test;']],
         action: 'executeRaw'
       })
-      const { children } = tx.trace.root
+      const children = tx.trace.getChildren(tx.trace.root.id)
       assert.equal(children.length, 2, 'should have 3 segments')
       const [firstSegment, secondSegment] = children
       assert.equal(firstSegment.name, 'Datastore/statement/Prisma/user/create')
diff --git a/test/unit/instrumentation/redis.test.js b/test/unit/instrumentation/redis.test.js
index 36fa840b83..5f53feefdf 100644
--- a/test/unit/instrumentation/redis.test.js
+++ b/test/unit/instrumentation/redis.test.js
@@ -184,7 +184,7 @@ test('createClient saves connection options', async function (t) {
     helper.runInTransaction(agent, async function (tx) {
       await client.queue.addCommand(['test', 'key', 'value'])
       await client2.queue.addCommand(['test2', 'key2', 'value2'])
-      const [redisSegment, redisSegment2] = tx.trace.root.children
+      const [redisSegment, redisSegment2] = tx.trace.getChildren(tx.trace.root.id)
       const attrs = redisSegment.getAttributes()
       assert.deepEqual(
         attrs,
diff --git a/test/unit/llm-events/openai/common.js b/test/unit/llm-events/openai/common.js
index 1eab799cb4..93310fb04b 100644
--- a/test/unit/llm-events/openai/common.js
+++ b/test/unit/llm-events/openai/common.js
@@ -45,18 +45,20 @@ const req = {
 
 function getExpectedResult(tx, event, type, completionId) {
   const trace = tx.trace.root
+  const [child] = tx.trace.getChildren(trace.id)
+  const spanId = child.id
   let expected = {
     'id': event.id,
     'appName': 'New Relic for Node.js tests',
     'request_id': 'req-id',
     'trace_id': tx.traceId,
-    'span_id': trace.children[0].id,
+    'span_id': spanId,
     'response.model': 'gpt-3.5-turbo-0613',
     'vendor': 'openai',
     'ingest_source': 'Node'
   }
   const resKeys = {
-    'duration': trace.children[0].getDurationInMillis(),
+    'duration': child.getDurationInMillis(),
     'request.model': 'gpt-3.5-turbo-0613',
     'response.organization': 'new-relic',
     'response.headers.llmVersion': '1.0.0',
diff --git a/test/unit/shim/datastore-shim.test.js b/test/unit/shim/datastore-shim.test.js
index 344367a5af..83c96766b8 100644
--- a/test/unit/shim/datastore-shim.test.js
+++ b/test/unit/shim/datastore-shim.test.js
@@ -33,14 +33,9 @@ test('DatastoreShim', async function (t) {
         return agent.tracer.getSegment()
       },
       withNested: function () {
-        const transaction = agent.tracer.getTransaction()
+        const tx = agent.tracer.getTransaction()
         const segment = agent.tracer.getSegment()
-        segment.add({
-          config: agent.config,
-          name: 'ChildSegment',
-          root: transaction.trace.root
-        })
-
+        tx.trace.add('ChildSegment', null, segment)
         return segment
       }
     }
@@ -381,13 +376,15 @@ test('DatastoreShim', async function (t) {
       shim.recordOperation(wrappable, 'withNested', () => {
         return new OperationSpec({ name: 'test', opaque: false })
       })
-      helper.runInTransaction(agent, () => {
+      helper.runInTransaction(agent, (tx) => {
         const startingSegment = agent.tracer.getSegment()
         const segment = wrappable.withNested()
         assert.notEqual(segment, startingSegment)
         assert.equal(segment.name, 'Datastore/operation/Cassandra/test')
-        assert.equal(segment.children.length, 1)
-        const [childSegment] = segment.children
+
+        const children = tx.trace.getChildren(segment.id)
+        assert.equal(children.length, 1)
+        const [childSegment] = children
         assert.equal(childSegment.name, 'ChildSegment')
         end()
       })
@@ -398,12 +395,13 @@ test('DatastoreShim', async function (t) {
       shim.recordOperation(wrappable, 'withNested', () => {
         return new OperationSpec({ name: 'test', opaque: true })
       })
-      helper.runInTransaction(agent, () => {
+      helper.runInTransaction(agent, (tx) => {
         const startingSegment = agent.tracer.getSegment()
         const segment = wrappable.withNested()
         assert.notEqual(segment, startingSegment)
         assert.equal(segment.name, 'Datastore/operation/Cassandra/test')
-        assert.equal(segment.children.length, 0)
+        const children = tx.trace.getChildren(segment.id)
+        assert.equal(children.length, 0)
         end()
       })
     })
@@ -778,7 +776,8 @@ test('DatastoreShim', async function (t) {
       helper.runInTransaction(agent, (tx) => {
         wrappable.bar()
         const rootSegment = agent.tracer.getSegment()
-        const attrs = rootSegment.children[0].getAttributes()
+        const [child] = tx.trace.getChildren(rootSegment.id)
+        const attrs = child.getAttributes()
         assert.equal(
           attrs['test-attr'],
           'unit-test',
@@ -923,7 +922,7 @@ test('DatastoreShim', async function (t) {
 
     await t.test('should create a new segment on the first call', function (t, end) {
       const { agent, shim, wrappable } = t.nr
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const args = [1, 2, wrappable.getActiveSegment]
         shim.bindRowCallbackSegment(args, shim.LAST)
 
@@ -931,14 +930,15 @@ test('DatastoreShim', async function (t) {
         const segment = shim.getSegment()
         const cbSegment = args[2]()
         assert.notEqual(cbSegment, segment)
-        assert.ok(segment.children.includes(cbSegment))
+        const children = tx.trace.getChildren(segment.id)
+        assert.ok(children.includes(cbSegment))
         end()
       })
     })
 
     await t.test('should not create a new segment for calls after the first', function (t, end) {
       const { agent, shim, wrappable } = t.nr
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const args = [1, 2, wrappable.getActiveSegment]
         shim.bindRowCallbackSegment(args, shim.LAST)
 
@@ -946,13 +946,15 @@ test('DatastoreShim', async function (t) {
         const segment = shim.getSegment()
         const cbSegment = args[2]()
         assert.notEqual(cbSegment, segment)
-        assert.ok(segment.children.includes(cbSegment))
-        assert.equal(segment.children.length, 1)
+        let children = tx.trace.getChildren(segment.id)
+        assert.ok(children.includes(cbSegment))
+        assert.equal(children.length, 1)
 
         // Call it a second time and see if we have the same segment.
         const cbSegment2 = args[2]()
         assert.equal(cbSegment2, cbSegment)
-        assert.equal(segment.children.length, 1)
+        children = tx.trace.getChildren(segment.id)
+        assert.equal(children.length, 1)
         end()
       })
     })
diff --git a/test/unit/shim/message-shim.test.js b/test/unit/shim/message-shim.test.js
index e81538540b..148ea75754 100644
--- a/test/unit/shim/message-shim.test.js
+++ b/test/unit/shim/message-shim.test.js
@@ -44,12 +44,7 @@ test('MessageShim', async function (t) {
       withNested: function () {
         const transaction = agent.tracer.getTransaction()
         const segment = agent.tracer.getSegment()
-        segment.add({
-          config: agent.config,
-          name: 'ChildSegment',
-          root: transaction.trace.root
-        })
-
+        transaction.trace.add('ChildSegment', null, segment)
         return segment
       }
     }
@@ -329,12 +324,13 @@ test('MessageShim', async function (t) {
         return new MessageSpec({ destinationName: 'foobar', opaque: false })
       })
 
-      helper.runInTransaction(agent, () => {
+      helper.runInTransaction(agent, (tx) => {
         const segment = wrappable.withNested()
         assert.equal(segment.name, 'MessageBroker/RabbitMQ/Exchange/Produce/Named/foobar')
 
-        assert.equal(segment.children.length, 1)
-        const [childSegment] = segment.children
+        const children = tx.trace.getChildren(segment.id)
+        assert.equal(children.length, 1)
+        const [childSegment] = children
         assert.equal(childSegment.name, 'ChildSegment')
         end()
       })
@@ -346,11 +342,12 @@ test('MessageShim', async function (t) {
         return new MessageSpec({ destinationName: 'foobar', opaque: true })
       })
 
-      helper.runInTransaction(agent, () => {
+      helper.runInTransaction(agent, (tx) => {
         const segment = wrappable.withNested()
         assert.equal(segment.name, 'MessageBroker/RabbitMQ/Exchange/Produce/Named/foobar')
 
-        assert.equal(segment.children.length, 0)
+        const children = tx.trace.getChildren(segment.id)
+        assert.equal(children.length, 0)
         end()
       })
     })
@@ -623,12 +620,13 @@ test('MessageShim', async function (t) {
         return new MessageSpec({ destinationName: 'foobar', opaque: false })
       })
 
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const segment = wrappable.withNested()
         assert.equal(segment.name, 'MessageBroker/RabbitMQ/Exchange/Consume/Named/foobar')
 
-        assert.equal(segment.children.length, 1)
-        const [childSegment] = segment.children
+        const children = tx.trace.getChildren(segment.id)
+        assert.equal(children.length, 1)
+        const [childSegment] = children
         assert.equal(childSegment.name, 'ChildSegment')
         end()
       })
@@ -640,10 +638,11 @@ test('MessageShim', async function (t) {
         return new MessageSpec({ destinationName: 'foobar', opaque: true })
       })
 
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const segment = wrappable.withNested()
         assert.equal(segment.name, 'MessageBroker/RabbitMQ/Exchange/Consume/Named/foobar')
-        assert.equal(segment.children.length, 0)
+        const children = tx.trace.getChildren(segment.id)
+        assert.equal(children.length, 0)
         end()
       })
     })
@@ -1213,11 +1212,12 @@ test('MessageShim', async function (t) {
 
     await t.test('should bind the subscribe callback', function (t, end) {
       const { agent, shim, wrapped } = t.nr
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
+        const { trace } = tx
         const parent = wrapped('my.queue', null, function subCb() {
           const segment = shim.getSegment()
           assert.equal(segment.name, 'Callback: subCb')
-          compareSegments(parent, [segment])
+          compareSegments({ parent, segments: [segment], trace })
           end()
         })
         assert.ok(parent)
diff --git a/test/unit/shim/shim.test.js b/test/unit/shim/shim.test.js
index aab0645c28..3563a9d4fe 100644
--- a/test/unit/shim/shim.test.js
+++ b/test/unit/shim/shim.test.js
@@ -1117,7 +1117,9 @@ test('Shim', async function (t) {
 
       stream.on('foobar', function () {
         const emitSegment = shim.getSegment()
-        assert.equal(emitSegment.parent, stream.segment)
+        const tx = agent.tracer.getTransaction()
+        const children = tx.trace.getChildren(stream.segment.id)
+        assert.ok(children.includes(emitSegment))
         end()
       })
 
@@ -1134,28 +1136,32 @@ test('Shim', async function (t) {
         return new RecorderSpec({ name: 'test segment', stream: 'foobar' })
       })
 
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const ret = wrapped()
         assert.equal(ret, stream)
         // Emit the event and check the segment name.
-        assert.equal(stream.segment.children.length, 0)
+        let children = tx.trace.getChildren(stream.segment.id)
+        assert.equal(children.length, 0)
         stream.emit('foobar')
-        assert.equal(stream.segment.children.length, 1)
+        children = tx.trace.getChildren(stream.segment.id)
+        assert.equal(children.length, 1)
 
-        const [eventSegment] = stream.segment.children
+        const [eventSegment] = children
         assert.match(eventSegment.name, /Event callback: foobar/)
         assert.equal(eventSegment.getAttributes().count, 1)
 
         // Emit it again and see if the name updated.
         stream.emit('foobar')
-        assert.equal(stream.segment.children.length, 1)
-        assert.equal(stream.segment.children[0], eventSegment)
+        children = tx.trace.getChildren(stream.segment.id)
+        assert.equal(children.length, 1)
+        assert.equal(children[0], eventSegment)
         assert.equal(eventSegment.getAttributes().count, 2)
 
         // Emit it once more and see if the name updated again.
         stream.emit('foobar')
-        assert.equal(stream.segment.children.length, 1)
-        assert.equal(stream.segment.children[0], eventSegment)
+        children = tx.trace.getChildren(stream.segment.id)
+        assert.equal(children.length, 1)
+        assert.equal(children[0], eventSegment)
         assert.equal(eventSegment.getAttributes().count, 3)
       })
     })
@@ -1567,12 +1573,14 @@ test('Shim', async function (t) {
         })
       })
 
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const parentSegment = shim.getSegment()
         const resultingSegment = wrapped(cb)
 
         assert.notEqual(resultingSegment, parentSegment)
-        assert.ok(parentSegment.children.includes(resultingSegment))
+
+        const children = tx.trace.getChildren(parentSegment.id)
+        assert.ok(children.includes(resultingSegment))
         end()
       })
     })
@@ -1593,12 +1601,13 @@ test('Shim', async function (t) {
         })
       })
 
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const parentSegment = shim.getSegment()
         const resultingSegment = wrapped()
 
         assert.equal(resultingSegment, parentSegment)
-        assert.ok(!parentSegment.children.includes(resultingSegment))
+        const children = tx.trace.getChildren(parentSegment.id)
+        assert.ok(!children.includes(resultingSegment))
         end()
       })
     })
@@ -2105,7 +2114,7 @@ test('Shim', async function (t) {
 
     await t.test('should create a new segment', function (t, end) {
       const { agent, shim, wrappable } = t.nr
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const args = [wrappable.getActiveSegment]
         const segment = wrappable.getActiveSegment()
         const parent = shim.createSegment({ name: 'test segment', parent: segment })
@@ -2114,7 +2123,7 @@ test('Shim', async function (t) {
 
         assert.notEqual(cbSegment, segment)
         assert.notEqual(cbSegment, parent)
-        compareSegments(parent, [cbSegment])
+        compareSegments({ parent, segments: [cbSegment], trace: tx.trace })
         end()
       })
     })
@@ -2129,7 +2138,7 @@ test('Shim', async function (t) {
         const cbSegment = args[0]()
 
         assert.notEqual(cbSegment, parent)
-        compareSegments(parent, [cbSegment])
+        compareSegments({ parent, segments: [cbSegment], trace: tx.trace })
         assert.equal(parent.opaque, false)
         end()
       })
@@ -2137,14 +2146,14 @@ test('Shim', async function (t) {
 
     await t.test('should default the `parentSegment` to the current one', function (t, end) {
       const { agent, shim, wrappable } = t.nr
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const args = [wrappable.getActiveSegment]
         const segment = wrappable.getActiveSegment()
         shim.bindCallbackSegment({}, args, shim.LAST)
         const cbSegment = args[0]()
 
         assert.notEqual(cbSegment, segment)
-        compareSegments(segment, [cbSegment])
+        compareSegments({ parent: segment, segments: [cbSegment], trace: tx.trace })
         end()
       })
     })
@@ -2157,14 +2166,14 @@ test('Shim', async function (t) {
           executed = true
         }
       }
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const args = [wrappable.getActiveSegment]
         const segment = wrappable.getActiveSegment()
         shim.bindCallbackSegment(spec, args, shim.LAST)
         const cbSegment = args[0]()
 
         assert.notEqual(cbSegment, segment)
-        compareSegments(segment, [cbSegment])
+        compareSegments({ parent: segment, segments: [cbSegment], trace: tx.trace })
         assert.equal(executed, true)
         end()
       })
@@ -2332,7 +2341,7 @@ test('Shim', async function (t) {
         const parent = shim.createSegment({ name: 'parent', parent: tx.trace.root })
         const child = shim.createSegment('child', parent)
         assert.equal(child.name, 'child')
-        compareSegments(parent, [child])
+        compareSegments({ parent, segments: [child], trace: tx.trace })
         end()
       })
     })
@@ -2343,7 +2352,7 @@ test('Shim', async function (t) {
         const parent = shim.createSegment('parent', tx.trace.root)
         const child = shim.createSegment('child', null, parent)
         assert.equal(child.name, 'child')
-        compareSegments(parent, [child])
+        compareSegments({ parent, segments: [child], trace: tx.trace })
         end()
       })
     })
@@ -2355,7 +2364,8 @@ test('Shim', async function (t) {
         parent.opaque = true
         const child = shim.createSegment('child', parent)
         assert.equal(child.name, 'parent')
-        assert.deepEqual(parent.children, [])
+        const children = tx.trace.getChildren(parent.id)
+        assert.deepEqual(children, [])
         end()
       })
     })
@@ -2378,10 +2388,10 @@ test('Shim', async function (t) {
 
     await t.test('should default to the current segment as the parent', function (t, end) {
       const { agent, shim } = t.nr
-      helper.runInTransaction(agent, function () {
+      helper.runInTransaction(agent, function (tx) {
         const parent = shim.getSegment()
         const child = shim.createSegment('child', parent)
-        compareSegments(parent, [child])
+        compareSegments({ parent, segments: [child], trace: tx.trace })
         end()
       })
     })
@@ -2408,7 +2418,7 @@ test('Shim', async function (t) {
         const parent = shim.createSegment('parent', tx.trace.root)
         const child = shim.createSegment({ name: 'child', parent })
         assert.equal(child.name, 'child')
-        compareSegments(parent, [child])
+        compareSegments({ parent, segments: [child], trace: tx.trace })
         end()
       })
     })
diff --git a/test/unit/spans/span-event.test.js b/test/unit/spans/span-event.test.js
index 33fd151c4d..be81e50fe8 100644
--- a/test/unit/spans/span-event.test.js
+++ b/test/unit/spans/span-event.test.js
@@ -62,7 +62,8 @@ test('fromSegment()', async (t) => {
       transaction.priority = 42
 
       setTimeout(() => {
-        const segment = agent.tracer.getTransaction().trace.root.children[0]
+        const tx = agent.tracer.getTransaction()
+        const [segment] = tx.trace.getChildren(tx.trace.root.id)
         segment.addSpanAttribute('SpiderSpan', 'web')
         segment.addSpanAttribute('host', 'my-host')
         segment.addSpanAttribute('port', 222)
@@ -135,7 +136,8 @@ test('fromSegment()', async (t) => {
       https.get('https://example.com?foo=bar', (res) => {
         res.resume()
         res.on('end', () => {
-          const segment = agent.tracer.getTransaction().trace.root.children[0]
+          const tx = agent.tracer.getTransaction()
+          const [segment] = tx.trace.getChildren(tx.trace.root.id)
           const span = SpanEvent.fromSegment(segment, transaction, 'parent')
 
           // Should have all the normal properties.
@@ -237,7 +239,7 @@ test('fromSegment()', async (t) => {
 
       dsConn.myDbOp(longQuery, () => {
         transaction.end()
-        const segment = transaction.trace.root.children[0]
+        const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
         const span = SpanEvent.fromSegment(segment, transaction, 'parent')
 
         // Should have all the normal properties.
@@ -357,7 +359,7 @@ test('fromSegment()', async (t) => {
 
         res.resume()
         res.on('end', () => {
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           assert.ok(segment.name.startsWith('Truncated'))
 
           const span = SpanEvent.fromSegment(segment, transaction)
diff --git a/test/unit/spans/streaming-span-event.test.js b/test/unit/spans/streaming-span-event.test.js
index 3e92aaa3cc..a92380ce71 100644
--- a/test/unit/spans/streaming-span-event.test.js
+++ b/test/unit/spans/streaming-span-event.test.js
@@ -60,7 +60,8 @@ test('fromSegment()', async (t) => {
       transaction.priority = 42
 
       setTimeout(() => {
-        const segment = agent.tracer.getTransaction().trace.root.children[0]
+        const tx = agent.tracer.getTransaction()
+        const [segment] = tx.trace.getChildren(tx.trace.root.id)
         const spanContext = segment.getSpanContext()
         spanContext.addCustomAttribute('Span Lee', 'no prize')
         segment.addSpanAttribute('host', 'my-host')
@@ -130,7 +131,8 @@ test('fromSegment()', async (t) => {
       https.get('https://example.com?foo=bar', (res) => {
         res.resume()
         res.on('end', () => {
-          const segment = agent.tracer.getTransaction().trace.root.children[0]
+          const tx = agent.tracer.getTransaction()
+          const [segment] = tx.trace.getChildren(tx.trace.root.id)
           const span = StreamingSpanEvent.fromSegment(segment, transaction, 'parent')
 
           // Should have all the normal properties.
@@ -237,7 +239,7 @@ test('fromSegment()', async (t) => {
 
       dsConn.myDbOp(longQuery, () => {
         transaction.end()
-        const segment = transaction.trace.root.children[0]
+        const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
         const span = StreamingSpanEvent.fromSegment(segment, transaction, 'parent')
 
         // Should have all the normal properties.
@@ -387,7 +389,7 @@ test('fromSegment()', async (t) => {
 
         res.resume()
         res.on('end', () => {
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           assert.ok(segment.name.startsWith('Truncated'))
 
           const span = StreamingSpanEvent.fromSegment(segment, transaction)
diff --git a/test/unit/transaction/trace/index.test.js b/test/unit/transaction/trace/index.test.js
index 46316f3236..ef98084175 100644
--- a/test/unit/transaction/trace/index.test.js
+++ b/test/unit/transaction/trace/index.test.js
@@ -119,7 +119,6 @@ test('Trace', async (t) => {
     child2.end()
     trace.root.end()
     transaction.end()
-    trace.generateSpanEvents()
 
     const events = agent.spanEventAggregator.getEvents()
     const nested = events[0]
@@ -232,7 +231,7 @@ test('Trace', async (t) => {
     transaction.acceptDistributedTraceHeaders('HTTP', headers)
 
     // Create at least one segment
-    const trace = new Trace(transaction)
+    const trace = transaction.trace
     const child = (transaction.baseSegment = trace.add('test'))
 
     child.start()
@@ -272,7 +271,7 @@ test('Trace', async (t) => {
     const transaction = new Transaction(agent)
     transaction.sampled = true
 
-    const trace = new Trace(transaction)
+    const trace = transaction.trace
 
     // add a child segment
     const child = (transaction.baseSegment = trace.add('test'))
@@ -311,7 +310,9 @@ test('when serializing synchronously', async (t) => {
 
   await t.test('should produce a transaction trace in the expected format', async (t) => {
     const { details } = t.nr
+    assert.equal(details.trace.segments.length, 3)
     const traceJSON = details.trace.generateJSONSync()
+    assert.equal(details.trace.segments.length, 0)
     const reconstituted = await codecDecodeAsync(traceJSON[4])
     assert.deepEqual(traceJSON, details.expectedEncoding, 'full trace JSON')
 
@@ -382,7 +383,9 @@ test('when serializing asynchronously', async (t) => {
 
   await t.test('should produce a transaction trace in the expected format', async (t) => {
     const { details } = t.nr
+    assert.equal(details.trace.segments.length, 3)
     const traceJSON = await details.trace.generateJSONAsync()
+    assert.equal(details.trace.segments.length, 0)
     const reconstituted = await codecDecodeAsync(traceJSON[4])
 
     assert.deepEqual(traceJSON, details.expectedEncoding, 'full trace JSON')
@@ -478,244 +481,158 @@ test('when inserting segments', async (t) => {
   })
 
   await t.test('should report total time', (t) => {
-    const { agent, trace } = t.nr
-    const root = trace.root
+    const { trace } = t.nr
     trace.setDurationInMillis(40, 0)
     const child = trace.add('Custom/Test18/Child1')
 
     child.setDurationInMillis(27, 0)
-    let seg = child.add({
-      config: agent.config,
-      name: 'UnitTest',
-      collect: true,
-      root
-    })
+    let seg = trace.add('UnitTest', null, child)
     seg.setDurationInMillis(9, 1)
-    seg = child.add({
-      config: agent.config,
-      name: 'UnitTest1',
-      collect: true,
-      root
-    })
+    seg = trace.add('UnitTest1', null, child)
     seg.setDurationInMillis(13, 1)
-    seg = child.add({
-      config: agent.config,
-      name: 'UnitTest2',
-      collect: true,
-      root
-    })
+    seg = trace.add('UnitTest2', null, child)
     seg.setDurationInMillis(9, 16)
-    seg = child.add({
-      config: agent.config,
-      name: 'UnitTest2',
-      collect: true,
-      root
-    })
+    seg = trace.add('UnitTest2', null, child)
     seg.setDurationInMillis(14, 16)
     assert.equal(trace.getTotalTimeDurationInMillis(), 48)
   })
 
   await t.test('should report total time on branched traces', (t) => {
-    const { trace, agent } = t.nr
-    const root = trace.root
+    const { trace } = t.nr
     trace.setDurationInMillis(40, 0)
-    const child = trace.add('Custom/Test18/Child1')
+    const child = trace.add('Custom/Test18/Child1', null, trace.root)
     child.setDurationInMillis(27, 0)
-    const seg1 = child.add({
-      config: agent.config,
-      name: 'UnitTest',
-      collect: true,
-      root
-    })
+    const seg1 = trace.add('UnitTest', null, child)
     seg1.setDurationInMillis(9, 1)
-    let seg = child.add({
-      config: agent.config,
-      name: 'UnitTest1',
-      collect: true,
-      root
-    })
+    let seg = trace.add('UnitTest1', null, child)
     seg.setDurationInMillis(13, 1)
-    seg = seg1.add({
-      config: agent.config,
-      name: 'UnitTest2',
-      collect: true,
-      root
-    })
+    seg = trace.add('UnitTest2', null, seg1)
     seg.setDurationInMillis(9, 16)
-    seg = seg1.add({
-      config: agent.config,
-      name: 'UnitTest2',
-      collect: true,
-      root
-    })
+    seg = trace.add('UnitTest2', null, seg1)
     seg.setDurationInMillis(14, 16)
     assert.equal(trace.getTotalTimeDurationInMillis(), 48)
   })
 
   await t.test('should report the expected trees for trees with uncollected segments', (t) => {
-    const { agent, trace } = t.nr
-    const root = trace.root
+    const { trace } = t.nr
     const expectedTrace = [
       0,
-      27,
-      'Root',
-      { nr_exclusive_duration_millis: 3 },
+      40,
+      'ROOT',
+      { nr_exclusive_duration_millis: 10 },
       [
         [
-          1,
-          10,
-          'first',
-          { nr_exclusive_duration_millis: 9 },
-          [[16, 25, 'first-first', { nr_exclusive_duration_millis: 9 }, []]]
-        ],
-        [
-          1,
-          14,
-          'second',
-          { nr_exclusive_duration_millis: 13 },
+          0,
+          27,
+          'Root',
+          { nr_exclusive_duration_millis: 3 },
           [
-            [16, 25, 'second-first', { nr_exclusive_duration_millis: 9 }, []],
-            [16, 25, 'second-second', { nr_exclusive_duration_millis: 9 }, []]
+            [
+              1,
+              10,
+              'first',
+              { nr_exclusive_duration_millis: 9 },
+              [[16, 25, 'first-first', { nr_exclusive_duration_millis: 9 }, []]]
+            ],
+            [
+              1,
+              14,
+              'second',
+              { nr_exclusive_duration_millis: 13 },
+              [
+                [16, 25, 'second-first', { nr_exclusive_duration_millis: 9 }, []],
+                [16, 25, 'second-second', { nr_exclusive_duration_millis: 9 }, []]
+              ]
+            ]
           ]
         ]
       ]
     ]
 
     trace.setDurationInMillis(40, 0)
-    const child = trace.add('Root')
+    const child = trace.add('Root', null, trace.root)
 
     child.setDurationInMillis(27, 0)
-    const seg1 = child.add({
-      config: agent.config,
-      name: 'first',
-      collect: true,
-      root
-    })
+    const seg1 = trace.add('first', null, child)
 
     seg1.setDurationInMillis(9, 1)
-    const seg2 = child.add({
-      config: agent.config,
-      name: 'second',
-      collect: true,
-      root
-    })
+    const seg2 = trace.add('second', null, child)
     seg2.setDurationInMillis(13, 1)
-    let seg = seg1.add({
-      config: agent.config,
-      name: 'first-first',
-      collect: true,
-      root
-    })
+    let seg = trace.add('first-first', null, seg1)
     seg.setDurationInMillis(9, 16)
-    seg = seg1.add({
-      config: agent.config,
-      name: 'first-second',
-      collect: true,
-      root
-    })
+    seg = trace.add('first-second', null, seg1)
     seg.setDurationInMillis(14, 16)
     seg._collect = false
-    seg = seg2.add({
-      config: agent.config,
-      name: 'second-first',
-      collect: true,
-      root
-    })
+    seg = trace.add('second-first', null, seg2)
     seg.setDurationInMillis(9, 16)
-    seg = seg2.add({
-      config: agent.config,
-      name: 'second-second',
-      collect: true,
-      root
-    })
+    seg = trace.add('second-second', null, seg2)
     seg.setDurationInMillis(9, 16)
 
     trace.end()
 
-    assert.deepEqual(child.toJSON(), expectedTrace)
+    assert.deepEqual(trace.toJSON(), expectedTrace)
   })
 
   await t.test('should report the expected trees for branched trees', (t) => {
-    const { agent, trace } = t.nr
+    const { trace } = t.nr
     const expectedTrace = [
       0,
-      27,
-      'Root',
-      { nr_exclusive_duration_millis: 3 },
+      40,
+      'ROOT',
+      { nr_exclusive_duration_millis: 10 },
       [
         [
-          1,
-          10,
-          'first',
-          { nr_exclusive_duration_millis: 9 },
+          0,
+          27,
+          'Root',
+          { nr_exclusive_duration_millis: 3 },
           [
-            [16, 25, 'first-first', { nr_exclusive_duration_millis: 9 }, []],
-            [16, 30, 'first-second', { nr_exclusive_duration_millis: 14 }, []]
-          ]
-        ],
-        [
-          1,
-          14,
-          'second',
-          { nr_exclusive_duration_millis: 13 },
-          [
-            [16, 25, 'second-first', { nr_exclusive_duration_millis: 9 }, []],
-            [16, 25, 'second-second', { nr_exclusive_duration_millis: 9 }, []]
+            [
+              1,
+              10,
+              'first',
+              { nr_exclusive_duration_millis: 9 },
+              [
+                [16, 25, 'first-first', { nr_exclusive_duration_millis: 9 }, []],
+                [16, 30, 'first-second', { nr_exclusive_duration_millis: 14 }, []]
+              ]
+            ],
+            [
+              1,
+              14,
+              'second',
+              { nr_exclusive_duration_millis: 13 },
+              [
+                [16, 25, 'second-first', { nr_exclusive_duration_millis: 9 }, []],
+                [16, 25, 'second-second', { nr_exclusive_duration_millis: 9 }, []]
+              ]
+            ]
           ]
         ]
       ]
     ]
+
     trace.setDurationInMillis(40, 0)
-    const child = trace.add('Root')
-    const root = trace.root
+    const child = trace.add('Root', null, trace.root)
 
     child.setDurationInMillis(27, 0)
-    const seg1 = child.add({
-      config: agent.config,
-      name: 'first',
-      collect: true,
-      root
-    })
+    const seg1 = trace.add('first', null, child)
+
     seg1.setDurationInMillis(9, 1)
-    const seg2 = child.add({
-      config: agent.config,
-      name: 'second',
-      collect: true,
-      root
-    })
+    const seg2 = trace.add('second', null, child)
     seg2.setDurationInMillis(13, 1)
-    let seg = seg1.add({
-      config: agent.config,
-      name: 'first-first',
-      collect: true,
-      root
-    })
+    let seg = trace.add('first-first', null, seg1)
     seg.setDurationInMillis(9, 16)
-    seg = seg1.add({
-      config: agent.config,
-      name: 'first-second',
-      collect: true,
-      root
-    })
+    seg = trace.add('first-second', null, seg1)
     seg.setDurationInMillis(14, 16)
-    seg = seg2.add({
-      config: agent.config,
-      name: 'second-first',
-      collect: true,
-      root
-    })
+    seg = trace.add('second-first', null, seg2)
     seg.setDurationInMillis(9, 16)
-    seg = seg2.add({
-      config: agent.config,
-      name: 'second-second',
-      collect: true,
-      root
-    })
+    seg = trace.add('second-second', null, seg2)
     seg.setDurationInMillis(9, 16)
 
     trace.end()
 
-    assert.deepEqual(child.toJSON(), expectedTrace)
+    assert.deepEqual(trace.toJSON(), expectedTrace)
   })
 
   await t.test('should measure exclusive time vs total time at each level of the graph', (t) => {
@@ -755,11 +672,10 @@ test('when inserting segments', async (t) => {
   })
 
   await t.test('should accurately sum overlapping subtrees', (t) => {
-    const { agent, trace } = t.nr
+    const { trace } = t.nr
     trace.setDurationInMillis(42)
 
     const now = Date.now()
-    const root = trace.root
 
     // create a long child on its own
     const child1 = trace.add('Custom/Test20/Child1')
@@ -767,39 +683,21 @@ test('when inserting segments', async (t) => {
     child1.setDurationInMillis(33, now)
 
     // add another, short child as a sibling
-    const child2 = child1.add({
-      config: agent.config,
-      name: 'Custom/Test20/Child2',
-      collect: true,
-      root
-    })
-
+    const child2 = trace.add('Custom/Test20/Child2', null, child1)
     child2.setDurationInMillis(5, now)
 
     // add two disjoint children of the second segment encompassed by the first segment
-    const child3 = child2.add({
-      config: agent.config,
-      name: 'Custom/Test20/Child3',
-      collect: true,
-      root
-    })
-
+    const child3 = trace.add('Custom/Test20/Child3', null, child2)
     child3.setDurationInMillis(11, now)
 
-    const child4 = child2.add({
-      config: agent.config,
-      name: 'Custom/Test20/Child3',
-      collect: true,
-      root
-    })
-
+    const child4 = trace.add('Custom/Test20/Child3', null, child2)
     child4.setDurationInMillis(11, now + 16)
 
     assert.equal(trace.getExclusiveDurationInMillis(), 9)
-    assert.equal(child4.getExclusiveDurationInMillis(), 11)
-    assert.equal(child3.getExclusiveDurationInMillis(), 11)
-    assert.equal(child2.getExclusiveDurationInMillis(), 0)
-    assert.equal(child1.getExclusiveDurationInMillis(), 11)
+    assert.equal(child4.getExclusiveDurationInMillis(trace), 11)
+    assert.equal(child3.getExclusiveDurationInMillis(trace), 11)
+    assert.equal(child2.getExclusiveDurationInMillis(trace), 0)
+    assert.equal(child1.getExclusiveDurationInMillis(trace), 11)
   })
 
   await t.test('should accurately sum partially overlapping segments', (t) => {
@@ -852,14 +750,53 @@ test('when inserting segments', async (t) => {
       }
     }
 
-    assert.equal(trace.root.children.length, 950)
+    assert.equal(trace.segments.length, 950)
     assert.equal(transaction._recorders.length, 950)
-    trace.segmentCount = 0
-    trace.root.children = []
-    trace.recorders = []
-
+    trace.end()
     function noop() {}
   })
+
+  await t.test('should not cause a stack overflow', { timeout: 30000 }, (t) => {
+    const { trace } = t.nr
+    for (let i = 0; i < 9000; ++i) {
+      trace.add(`Child ${i}`)
+    }
+
+    assert.doesNotThrow(function () {
+      trace.toJSON()
+    })
+  })
+
+  await t.test('should get all children for a segment', (t) => {
+    const { trace } = t.nr
+    assert.deepEqual(trace.segments, [])
+    const segment = trace.add('base')
+    const segment2 = trace.add('1', null, segment)
+    const segment3 = trace.add('2', null, segment)
+    const children = trace.getChildren(segment.id)
+    assert.deepEqual(children, [segment2, segment3])
+  })
+
+  await t.test('should get all collected children for a segment', (t) => {
+    const { trace } = t.nr
+    const segment = trace.add('base')
+    const segment2 = trace.add('1', null, segment)
+    const segment3 = trace.add('2', null, segment)
+    const segment4 = trace.add('3', null, segment)
+    segment4._collect = false
+    const segment5 = trace.add('4', null, segment)
+    segment5.ignore = true
+    const children = trace.getCollectedChildren(segment.id)
+    assert.deepEqual(children, [segment2, segment3])
+  })
+
+  await t.test('should get parent segment for a segment', (t) => {
+    const { trace } = t.nr
+    const segment = trace.add('base')
+    const segment2 = trace.add('1', null, segment)
+    const parent = trace.getParent(segment2.parentId)
+    assert.equal(parent, segment)
+  })
 })
 
 test('should set URI to null when request.uri attribute is excluded globally', async (t) => {
@@ -1031,14 +968,9 @@ test('infinite tracing', async (t) => {
 
 function addTwoSegments(transaction) {
   const trace = transaction.trace
-  const root = trace.root
   const child1 = (transaction.baseSegment = trace.add('test'))
   child1.start()
-  const child2 = child1.add({
-    config: transaction.agent.config,
-    name: 'nested',
-    root
-  })
+  const child2 = trace.add('nested', null, child1)
   child2.start()
   child1.end()
   child2.end()
@@ -1057,10 +989,7 @@ async function makeTrace(agent) {
   transaction.url = URL
   transaction.verb = 'GET'
 
-  transaction.timer.setDurationInMillis(DURATION)
-
   const trace = transaction.trace
-  const root = trace.root
 
   // promisifying `trace.generateJSON` so tests do not have to call done
   // and instead use async/await
@@ -1069,33 +998,19 @@ async function makeTrace(agent) {
   assert.ok(start > 0, "root segment's start time")
   trace.setDurationInMillis(DURATION, 0)
 
-  const web = trace.root.add({
-    config: agent.config,
-    name: URL,
-    collect: true,
-    root
-  })
+  const web = trace.add(URL)
   transaction.baseSegment = web
   transaction.finalizeNameFromUri(URL, 200)
   // top-level element will share a duration with the quasi-ROOT node
   web.setDurationInMillis(DURATION, 0)
 
-  const db = web.add({
-    config: agent.config,
-    name: 'Database/statement/AntiSQL/select/getSome',
-    collect: true,
-    root
-  })
+  const db = trace.add('Database/statement/AntiSQL/select/getSome', null, web)
   db.setDurationInMillis(14, 3)
 
-  const memcache = web.add({
-    config: agent.config,
-    name: 'Datastore/operation/Memcache/lookup',
-    collect: true,
-    root
-  })
+  const memcache = trace.add('Datastore/operation/Memcache/lookup', null, web)
   memcache.setDurationInMillis(20, 8)
 
+  transaction.timer.setDurationInMillis(DURATION)
   trace.end()
 
   /*
@@ -1103,6 +1018,21 @@ async function makeTrace(agent) {
    * outermost version having its scope always set to 'ROOT'. The null bits
    * are parameters, which are optional, and so far, unimplemented for Node.
    */
+  const dbSegment = [
+    3,
+    17,
+    'Database/statement/AntiSQL/select/getSome',
+    { nr_exclusive_duration_millis: 14 },
+    []
+  ]
+  const memcacheSegment = [
+    8,
+    28,
+    'Datastore/operation/Memcache/lookup',
+    { nr_exclusive_duration_millis: 20 },
+    []
+  ]
+
   const rootSegment = [
     0,
     DURATION,
@@ -1118,15 +1048,10 @@ async function makeTrace(agent) {
           'request.parameters.test': 'value',
           'nr_exclusive_duration_millis': 8
         },
-        [
-          // TODO: ensure that the ordering is correct WRT start time
-          db.toJSON(),
-          memcache.toJSON()
-        ]
+        [dbSegment, memcacheSegment]
       ]
     ]
   ]
-
   const rootNode = [
     trace.root.timer.start / 1000,
     {},
@@ -1147,7 +1072,6 @@ async function makeTrace(agent) {
   return {
     transaction,
     trace,
-    rootSegment,
     rootNode,
     expectedEncoding: [
       0,
diff --git a/test/unit/transaction/trace/segment.test.js b/test/unit/transaction/trace/segment.test.js
index c8bcf63e6c..1522734fe0 100644
--- a/test/unit/transaction/trace/segment.test.js
+++ b/test/unit/transaction/trace/segment.test.js
@@ -27,36 +27,6 @@ test('TraceSegment', async (t) => {
   t.beforeEach(beforeEach)
   t.afterEach(afterEach)
 
-  await t.test('should not add new children when marked as opaque', (t) => {
-    const { agent } = t.nr
-    const trans = new Transaction(agent)
-    const root = trans.trace.root
-    const segment = new TraceSegment({
-      config: agent.config,
-      name: 'UnitTest',
-      collect: true,
-      root
-    })
-    assert.ok(!segment.opaque)
-    segment.opaque = true
-    segment.add({
-      config: agent.config,
-      name: 'child',
-      collect: true,
-      root
-    })
-    assert.equal(segment.children.length, 0)
-    segment.opaque = false
-    segment.add({
-      config: agent.config,
-      name: 'child',
-      collect: true,
-      root
-    })
-    assert.equal(segment.children.length, 1)
-    trans.end()
-  })
-
   await t.test('has a name', (t) => {
     const { agent } = t.nr
     const trans = new Transaction(agent)
@@ -70,19 +40,6 @@ test('TraceSegment', async (t) => {
     assert.equal(success.name, 'UnitTest')
   })
 
-  await t.test('is created with no children', (t) => {
-    const { agent } = t.nr
-    const trans = new Transaction(agent)
-    const root = trans.trace.root
-    const segment = new TraceSegment({
-      config: agent.config,
-      name: 'UnitTest',
-      collect: true,
-      root
-    })
-    assert.equal(segment.children.length, 0)
-  })
-
   await t.test('has a timer', (t) => {
     const { agent } = t.nr
     const trans = new Transaction(agent)
@@ -202,7 +159,7 @@ test('TraceSegment', async (t) => {
       collect: true,
       root
     })
-    segment.toJSON()
+    transaction.trace.toJSON()
     assert.deepEqual(segment.getAttributes(), {})
   })
 
@@ -231,8 +188,9 @@ test('TraceSegment', async (t) => {
 
     trace.end()
 
-    // See documentation on TraceSegment.toJSON for what goes in which field.
-    assert.deepEqual(segment.toJSON(), [
+    // get serialized segment from trace
+    const serializedSegment = trace.toJSON()[4][0]
+    assert.deepEqual(serializedSegment, [
       3,
       17,
       'DB/select/getSome',
@@ -283,7 +241,7 @@ test('TraceSegment', async (t) => {
     segment.timer.start = 1001
     segment.overwriteDurationInMillis(3)
 
-    segment.finalize()
+    segment.finalize(transaction.trace)
 
     assert.equal(segment.name, `Truncated/${segmentName}`)
     assert.equal(root.getDurationInMillis(), 4)
@@ -299,17 +257,9 @@ test('with children created from URLs', async (t) => {
 
     const transaction = new Transaction(ctx.nr.agent)
     const trace = transaction.trace
-    const root = transaction.trace.root
-    const segment = trace.add('UnitTest')
-
     const url = '/test?test1=value1&test2&test3=50&test4='
 
-    const webChild = segment.add({
-      config: ctx.nr.agent,
-      name: url,
-      collect: true,
-      root
-    })
+    const webChild = trace.add(url)
     transaction.baseSegment = webChild
     transaction.finalizeNameFromUri(url, 200)
 
@@ -318,6 +268,7 @@ test('with children created from URLs', async (t) => {
 
     trace.end()
     ctx.nr.webChild = webChild
+    ctx.nr.trace = trace
   })
 
   t.afterEach(afterEach)
@@ -350,8 +301,10 @@ test('with children created from URLs', async (t) => {
   })
 
   await t.test('should serialize the segment with the parameters', (t) => {
-    const { webChild } = t.nr
-    assert.deepEqual(webChild.toJSON(), [
+    const { trace } = t.nr
+    // get serialized segment from trace
+    const serializedSegment = trace.toJSON()[4][0]
+    assert.deepEqual(serializedSegment, [
       0,
       1,
       'WebTransaction/NormalizedUri/*',
@@ -374,11 +327,8 @@ test('with parameters parsed out by framework', async (t) => {
 
     const transaction = new Transaction(ctx.nr.agent)
     const trace = transaction.trace
-    const root = trace.root
     trace.mer = 6
 
-    const segment = trace.add('UnitTest')
-
     const url = '/test'
     const params = {}
 
@@ -387,12 +337,7 @@ test('with parameters parsed out by framework', async (t) => {
     params[1] = 'another'
     params.test3 = '50'
 
-    const webChild = segment.add({
-      config: ctx.nr.agent.config,
-      name: url,
-      collect: true,
-      root
-    })
+    const webChild = trace.add(url)
     transaction.trace.attributes.addAttributes(DESTINATIONS.TRANS_SCOPE, params)
     transaction.baseSegment = webChild
     transaction.finalizeNameFromUri(url, 200)
@@ -429,7 +374,7 @@ test('with parameters parsed out by framework', async (t) => {
   })
 
   await t.test('should serialize the segment with the parameters', (t) => {
-    const { webChild } = t.nr
+    const { trace } = t.nr
     const expected = [
       0,
       1,
@@ -442,7 +387,9 @@ test('with parameters parsed out by framework', async (t) => {
       },
       []
     ]
-    assert.deepEqual(webChild.toJSON(), expected)
+    // get serialized segment from trace
+    const serializedSegment = trace.toJSON()[4][0]
+    assert.deepEqual(serializedSegment, expected)
   })
 })
 
@@ -453,17 +400,9 @@ test('with attributes.enabled set to false', async (t) => {
 
     const transaction = new Transaction(ctx.nr.agent)
     const trace = transaction.trace
-    const root = trace.root
-    const segment = trace.add('UnitTest')
     const url = '/test?test1=value1&test2&test3=50&test4='
 
-    const webChild = segment.add({
-      config: ctx.nr.agent.config,
-      name: url,
-      collect: true,
-
-      root
-    })
+    const webChild = trace.add(url)
     webChild.addAttribute('test', 'non-null value')
     transaction.baseSegment = webChild
     transaction.finalizeNameFromUri(url, 200)
@@ -471,6 +410,7 @@ test('with attributes.enabled set to false', async (t) => {
     trace.setDurationInMillis(1, 0)
     webChild.setDurationInMillis(1, 0)
     ctx.nr.webChild = webChild
+    ctx.nr.trace = trace
   })
   t.afterEach(afterEach)
 
@@ -485,9 +425,11 @@ test('with attributes.enabled set to false', async (t) => {
   })
 
   await t.test('should serialize the segment without the parameters', (t) => {
-    const { webChild } = t.nr
+    const { trace } = t.nr
     const expected = [0, 1, 'WebTransaction/NormalizedUri/*', {}, []]
-    assert.deepEqual(webChild.toJSON(), expected)
+    // get serialized segment from trace
+    const serializedSegment = trace.toJSON()[4][0]
+    assert.deepEqual(serializedSegment, expected)
   })
 })
 
@@ -504,18 +446,9 @@ test('with attributes.enabled set', async (t) => {
 
     const transaction = new Transaction(ctx.nr.agent)
     const trace = transaction.trace
-    const root = trace.root
-    const segment = trace.add('UnitTest')
-
     const url = '/test?test1=value1&test2&test3=50&test4='
 
-    const webChild = segment.add({
-      config: ctx.nr.agent.config,
-      name: url,
-      collect: true,
-
-      root
-    })
+    const webChild = trace.add(url)
     transaction.baseSegment = webChild
     transaction.finalizeNameFromUri(url, 200)
     webChild.markAsWeb(transaction)
@@ -526,6 +459,7 @@ test('with attributes.enabled set', async (t) => {
     ctx.nr.webChild = webChild
 
     trace.end()
+    ctx.nr.trace = trace
   })
   t.afterEach(afterEach)
 
@@ -558,8 +492,10 @@ test('with attributes.enabled set', async (t) => {
   })
 
   await t.test('should serialize the segment with the parameters', (t) => {
-    const { webChild } = t.nr
-    assert.deepEqual(webChild.toJSON(), [
+    const { trace } = t.nr
+    // get the specific segment from serialized trace
+    const serializedSegment = trace.toJSON()[4][0]
+    assert.deepEqual(serializedSegment, [
       0,
       1,
       'WebTransaction/NormalizedUri/*',
@@ -578,12 +514,7 @@ test('when serialized', async (t) => {
     const agent = helper.loadMockedAgent()
     const transaction = new Transaction(agent)
     const root = transaction.trace.root
-    const segment = new TraceSegment({
-      config: agent.config,
-      name: 'UnitTest',
-      collect: true,
-      root
-    })
+    const segment = transaction.trace.add('UnitTest')
 
     ctx.nr = {
       agent: agent,
@@ -600,9 +531,9 @@ test('when serialized', async (t) => {
   })
 
   await t.test('should create a plain JS array', (t) => {
-    const { segment } = t.nr
+    const { segment, transaction } = t.nr
     segment.end()
-    const js = segment.toJSON()
+    const js = transaction.trace.toJSON()[4][0]
 
     assert.ok(Array.isArray(js))
     assert.equal(typeof js[0], 'number')
@@ -615,25 +546,6 @@ test('when serialized', async (t) => {
     assert.ok(Array.isArray(js[4]))
     assert.equal(js[4].length, 0)
   })
-
-  await t.test('should not cause a stack overflow', { timeout: 30000 }, (t) => {
-    const { segment, agent, root } = t.nr
-    let parent = segment
-    for (let i = 0; i < 9000; ++i) {
-      const child = new TraceSegment({
-        config: agent.config,
-        name: 'Child ' + i,
-        collect: true,
-        root
-      })
-      parent.children.push(child)
-      parent = child
-    }
-
-    assert.doesNotThrow(function () {
-      segment.toJSON()
-    })
-  })
 })
 
 test('getSpanContext', async (t) => {
diff --git a/test/unit/transaction/tracer.test.js b/test/unit/transaction/tracer.test.js
index a2ecbd101f..fb8298eba4 100644
--- a/test/unit/transaction/tracer.test.js
+++ b/test/unit/transaction/tracer.test.js
@@ -209,11 +209,13 @@ test('Tracer', async function (t) {
       assert.equal(agent.segmentsCreatedInHarvest, 1)
       assert.equal(tx.numSegments, 1)
       assert.equal(agent.activeTransactions, 1)
+      assert.equal(tx.trace.segments.length, 0)
 
       tracer.createSegment({ name: 'Test', parent: tx.trace.root, transaction: tx })
       assert.equal(agent.totalActiveSegments, 2)
       assert.equal(agent.segmentsCreatedInHarvest, 2)
       assert.equal(tx.numSegments, 2)
+      assert.equal(tx.trace.segments.length, 1)
       tx.end()
 
       assert.equal(agent.activeTransactions, 0)
@@ -230,5 +232,18 @@ test('Tracer', async function (t) {
         })
       }, 10)
     })
+    await t.test('skip adding children when parent is opaque', (t) => {
+      const { agent, tracer } = t.nr
+      const tx = new Transaction(agent)
+      tracer.setSegment({ transaction: tx, segment: tx.trace.root })
+      const segment = tracer.createSegment({ name: 'Test', parent: tx.trace.root, transaction: tx })
+      segment.opaque = true
+      const segment2 = tracer.createSegment({ name: 'Test1', parent: segment, transaction: tx })
+      const segment3 = tracer.createSegment({ name: 'Test2', parent: segment, transaction: tx })
+      assert.equal(segment2.id, segment.id)
+      assert.equal(segment3.id, segment.id)
+      assert.equal(tx.trace.segments.length, 1)
+      tx.end()
+    })
   })
 })
diff --git a/test/versioned-external/external-repos.js b/test/versioned-external/external-repos.js
index c71fb25c2b..ac3ab1c73a 100644
--- a/test/versioned-external/external-repos.js
+++ b/test/versioned-external/external-repos.js
@@ -15,7 +15,7 @@ const repos = [
   {
     name: 'apollo-server',
     repository: 'https://github.com/newrelic/newrelic-node-apollo-server-plugin.git',
-    branch: 'remove-transaction-from-segment',
+    branch: 'remove-child-segments',
     additionalFiles: [
       'tests/lib',
     ]
diff --git a/test/versioned/amqplib/amqp-utils.js b/test/versioned/amqplib/amqp-utils.js
index c0ac47fcce..19a363753f 100644
--- a/test/versioned/amqplib/amqp-utils.js
+++ b/test/versioned/amqplib/amqp-utils.js
@@ -27,7 +27,7 @@ exports.verifyTransaction = verifyTransaction
 exports.getChannel = getChannel
 
 function verifySubscribe(tx, exchange, routingKey) {
-  const isCallback = !!metrics.findSegment(tx.trace.root, 'Callback: <anonymous>')
+  const isCallback = !!metrics.findSegment(tx.trace, tx.trace.root, 'Callback: <anonymous>')
 
   let segments = []
 
@@ -39,7 +39,7 @@ function verifySubscribe(tx, exchange, routingKey) {
     segments = ['MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchange]
   }
 
-  assertSegments(tx.trace.root, segments)
+  assertSegments(tx.trace, tx.trace.root, segments)
 
   assertMetrics(
     tx.metrics,
@@ -51,6 +51,7 @@ function verifySubscribe(tx, exchange, routingKey) {
   assert.equal(tx.getFullName(), null, 'should not set transaction name')
 
   const consume = metrics.findSegment(
+    tx.trace,
     tx.trace.root,
     'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchange
   )
@@ -91,7 +92,7 @@ function verifyDistributedTrace(produceTransaction, consumeTransaction) {
     consumeTransaction.traceId,
     'should have proper trace id'
   )
-  const produceSegment = produceTransaction.trace.root.children[0]
+  const [produceSegment] = produceTransaction.trace.getChildren(produceTransaction.trace.root.id)
   assert.equal(
     produceSegment.id,
     consumeTransaction.parentSpanId,
@@ -121,6 +122,7 @@ function verifyConsumeTransaction(tx, exchange, queue, routingKey) {
   )
 
   const consume = metrics.findSegment(
+    tx.trace,
     tx.trace.root,
     'OtherTransaction/Message/RabbitMQ/Exchange/Named/' + exchange
   )
@@ -143,7 +145,7 @@ function verifyConsumeTransaction(tx, exchange, queue, routingKey) {
 }
 
 function verifySendToQueue(tx) {
-  assertSegments(tx.trace.root, ['MessageBroker/RabbitMQ/Exchange/Produce/Named/Default'])
+  assertSegments(tx.trace, tx.trace.root, ['MessageBroker/RabbitMQ/Exchange/Produce/Named/Default'])
 
   assertMetrics(
     tx.metrics,
@@ -153,6 +155,7 @@ function verifySendToQueue(tx) {
   )
 
   const segment = metrics.findSegment(
+    tx.trace,
     tx.trace.root,
     'MessageBroker/RabbitMQ/Exchange/Produce/Named/Default'
   )
@@ -165,7 +168,7 @@ function verifySendToQueue(tx) {
 }
 
 function verifyProduce(tx, exchangeName, routingKey) {
-  const isCallback = !!metrics.findSegment(tx.trace.root, 'Callback: <anonymous>')
+  const isCallback = !!metrics.findSegment(tx.trace, tx.trace.root, 'Callback: <anonymous>')
   let segments = []
 
   if (isCallback) {
@@ -197,7 +200,7 @@ function verifyProduce(tx, exchangeName, routingKey) {
     ]
   }
 
-  assertSegments(tx.trace.root, segments, 'should have expected segments')
+  assertSegments(tx.trace, tx.trace.root, segments, 'should have expected segments')
 
   assertMetrics(
     tx.metrics,
@@ -207,6 +210,7 @@ function verifyProduce(tx, exchangeName, routingKey) {
   )
 
   const segment = metrics.findSegment(
+    tx.trace,
     tx.trace.root,
     'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchangeName
   )
@@ -222,17 +226,17 @@ function verifyProduce(tx, exchangeName, routingKey) {
 }
 
 function verifyGet({ tx, exchangeName, routingKey, queue, assertAttr }) {
-  const isCallback = !!metrics.findSegment(tx.trace.root, 'Callback: <anonymous>')
+  const isCallback = !!metrics.findSegment(tx.trace, tx.trace.root, 'Callback: <anonymous>')
   const produceName = 'MessageBroker/RabbitMQ/Exchange/Produce/Named/' + exchangeName
   const consumeName = 'MessageBroker/RabbitMQ/Exchange/Consume/Named/' + queue
   if (isCallback) {
-    assertSegments(tx.trace.root, [produceName, consumeName, ['Callback: <anonymous>']])
+    assertSegments(tx.trace, tx.trace.root, [produceName, consumeName, ['Callback: <anonymous>']])
   } else {
-    assertSegments(tx.trace.root, [produceName, consumeName])
+    assertSegments(tx.trace, tx.trace.root, [produceName, consumeName])
   }
   assertMetrics(tx.metrics, [[{ name: produceName }], [{ name: consumeName }]], false, false)
   if (assertAttr) {
-    const segment = metrics.findSegment(tx.trace.root, consumeName)
+    const segment = metrics.findSegment(tx.trace, tx.trace.root, consumeName)
     const attributes = segment.getAttributes()
     assert.equal(attributes.host, params.rabbitmq_host, 'should have host on segment')
     assert.equal(attributes.port, params.rabbitmq_port, 'should have port on segment')
@@ -241,7 +245,7 @@ function verifyGet({ tx, exchangeName, routingKey, queue, assertAttr }) {
 }
 
 function verifyPurge(tx) {
-  const isCallback = !!metrics.findSegment(tx.trace.root, 'Callback: <anonymous>')
+  const isCallback = !!metrics.findSegment(tx.trace, tx.trace.root, 'Callback: <anonymous>')
   let segments = []
 
   if (isCallback) {
@@ -272,7 +276,7 @@ function verifyPurge(tx) {
       'MessageBroker/RabbitMQ/Queue/Purge/Temp'
     ]
   }
-  assertSegments(tx.trace.root, segments, 'should have expected segments')
+  assertSegments(tx.trace, tx.trace.root, segments, 'should have expected segments')
 
   assertMetrics(tx.metrics, [[{ name: 'MessageBroker/RabbitMQ/Queue/Purge/Temp' }]], false, false)
 }
diff --git a/test/versioned/amqplib/callback.test.js b/test/versioned/amqplib/callback.test.js
index 9d1e7e87f6..f14f635125 100644
--- a/test/versioned/amqplib/callback.test.js
+++ b/test/versioned/amqplib/callback.test.js
@@ -74,7 +74,7 @@ test('amqplib callback instrumentation', async function (t) {
     helper.runInTransaction(agent, function (tx) {
       amqplib.connect(amqpUtils.CON_STRING, null, function (err, _conn) {
         assert.ok(!err, 'should not break connection')
-        const [segment] = tx.trace.root.children
+        const [segment] = tx.trace.getChildren(tx.trace.root.id)
         assert.equal(segment.name, 'amqplib.connect')
         const attrs = segment.getAttributes()
         assert.equal(attrs.host, 'localhost')
diff --git a/test/versioned/amqplib/promises.test.js b/test/versioned/amqplib/promises.test.js
index 7999e04a6d..3ab386cdf7 100644
--- a/test/versioned/amqplib/promises.test.js
+++ b/test/versioned/amqplib/promises.test.js
@@ -65,7 +65,7 @@ test('amqplib promise instrumentation', async function (t) {
     const { agent, amqplib } = t.nr
     await helper.runInTransaction(agent, async function (tx) {
       const _conn = await amqplib.connect(amqpUtils.CON_STRING)
-      const [segment] = tx.trace.root.children
+      const [segment] = tx.trace.getChildren(tx.trace.root.id)
       assert.equal(segment.name, 'amqplib.connect')
       const attrs = segment.getAttributes()
       assert.equal(attrs.host, 'localhost')
diff --git a/test/versioned/aws-sdk-v2/amazon-dax-client.test.js b/test/versioned/aws-sdk-v2/amazon-dax-client.test.js
index 6070538c7f..b5ee8bab88 100644
--- a/test/versioned/aws-sdk-v2/amazon-dax-client.test.js
+++ b/test/versioned/aws-sdk-v2/amazon-dax-client.test.js
@@ -61,11 +61,19 @@ test('amazon-dax-client', async (t) => {
         const root = transaction.trace.root
 
         // Won't have the attributes cause not making web request...
-        const segments = common.getMatchingSegments(root, common.DATASTORE_PATTERN)
+        const segments = common.getMatchingSegments({
+          trace: transaction.trace,
+          segment: root,
+          pattern: common.DATASTORE_PATTERN
+        })
 
         assert.equal(segments.length, 1)
 
-        const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+        const externalSegments = common.checkAWSAttributes({
+          trace: transaction.trace,
+          segment: root,
+          pattern: common.EXTERN_PATTERN
+        })
         assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
         const segment = segments[0]
diff --git a/test/versioned/aws-sdk-v2/dynamodb.test.js b/test/versioned/aws-sdk-v2/dynamodb.test.js
index 1a27862846..658e534b3b 100644
--- a/test/versioned/aws-sdk-v2/dynamodb.test.js
+++ b/test/versioned/aws-sdk-v2/dynamodb.test.js
@@ -92,11 +92,19 @@ test('DynamoDB', async (t) => {
 
 function finish(end, tests, tx) {
   const root = tx.trace.root
-  const segments = common.checkAWSAttributes(root, common.DATASTORE_PATTERN)
+  const segments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.DATASTORE_PATTERN
+  })
 
   assert.equal(segments.length, tests.length, `should have ${tests.length} aws datastore segments`)
 
-  const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+  const externalSegments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
   segments.forEach((segment, i) => {
diff --git a/test/versioned/aws-sdk-v2/http-services.test.js b/test/versioned/aws-sdk-v2/http-services.test.js
index ecf9e525df..951c9a08c4 100644
--- a/test/versioned/aws-sdk-v2/http-services.test.js
+++ b/test/versioned/aws-sdk-v2/http-services.test.js
@@ -238,7 +238,11 @@ test('AWS HTTP Services', async (t) => {
 })
 
 function finish(end, service, operation, tx) {
-  const externals = common.checkAWSAttributes(tx.trace.root, common.EXTERN_PATTERN)
+  const externals = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: tx.trace.root,
+    pattern: common.EXTERN_PATTERN
+  })
   if (assert.equal(externals.length, 1, 'should have an aws external')) {
     const attrs = externals[0].attributes.get(common.SEGMENT_DESTINATION)
     match(attrs, {
diff --git a/test/versioned/aws-sdk-v2/s3.test.js b/test/versioned/aws-sdk-v2/s3.test.js
index 50a2fe6d84..69a45ae9e5 100644
--- a/test/versioned/aws-sdk-v2/s3.test.js
+++ b/test/versioned/aws-sdk-v2/s3.test.js
@@ -82,7 +82,11 @@ test('S3 buckets', async (t) => {
 })
 
 function finish(end, tx) {
-  const externals = common.checkAWSAttributes(tx.trace.root, common.EXTERN_PATTERN)
+  const externals = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: tx.trace.root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externals.length, 3, 'should have 3 aws externals')
   const [head, create, del] = externals
   checkAttrs(head, 'headBucket')
diff --git a/test/versioned/aws-sdk-v2/sns.test.js b/test/versioned/aws-sdk-v2/sns.test.js
index 372cb8e71d..a2629bba87 100644
--- a/test/versioned/aws-sdk-v2/sns.test.js
+++ b/test/versioned/aws-sdk-v2/sns.test.js
@@ -72,10 +72,18 @@ test('SNS', async (t) => {
 function finish(end, tx) {
   const root = tx.trace.root
 
-  const messages = common.checkAWSAttributes(root, common.SNS_PATTERN)
+  const messages = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.SNS_PATTERN
+  })
   assert.equal(messages.length, 1, 'should have 1 message broker segment')
 
-  const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+  const externalSegments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
   const attrs = messages[0].attributes.get(common.SEGMENT_DESTINATION)
diff --git a/test/versioned/aws-sdk-v2/sqs.test.js b/test/versioned/aws-sdk-v2/sqs.test.js
index 4b97825b85..733d8fca88 100644
--- a/test/versioned/aws-sdk-v2/sqs.test.js
+++ b/test/versioned/aws-sdk-v2/sqs.test.js
@@ -154,7 +154,11 @@ function finish({
   const expectedSegmentCount = 3
 
   const root = transaction.trace.root
-  const segments = common.checkAWSAttributes(root, common.SQS_PATTERN)
+  const segments = common.checkAWSAttributes({
+    trace: transaction.trace,
+    segment: root,
+    pattern: common.SQS_PATTERN
+  })
 
   assert.equal(
     segments.length,
@@ -162,7 +166,11 @@ function finish({
     `should have ${expectedSegmentCount} AWS MessageBroker/SQS segments`
   )
 
-  const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+  const externalSegments = common.checkAWSAttributes({
+    trace: transaction.trace,
+    segment: root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
   const [sendMessage, sendMessageBatch, receiveMessage] = segments
diff --git a/test/versioned/aws-sdk-v3/bedrock-chat-completions.test.js b/test/versioned/aws-sdk-v3/bedrock-chat-completions.test.js
index ffc90e570e..8ab8a67c60 100644
--- a/test/versioned/aws-sdk-v3/bedrock-chat-completions.test.js
+++ b/test/versioned/aws-sdk-v3/bedrock-chat-completions.test.js
@@ -106,6 +106,7 @@ test.afterEach(afterEach)
       assert.equal(response.$metadata.requestId, expected.headers['x-amzn-requestid'])
       assert.deepEqual(body, expected.body)
       assertSegments(
+        tx.trace,
         tx.trace.root,
         ['Llm/completion/Bedrock/InvokeModelCommand', [expectedExternalPath(modelId)]],
         { exact: false }
@@ -297,6 +298,7 @@ test.afterEach(afterEach)
       })
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         ['Llm/completion/Bedrock/InvokeModelCommand', [expectedExternalPath(modelId)]],
         { exact: false }
@@ -434,6 +436,7 @@ test(`ai21: should properly create errors on create completion (streamed)`, asyn
     })
 
     assertSegments(
+      tx.trace,
       tx.trace.root,
       [
         'Llm/completion/Bedrock/InvokeModelWithResponseStreamCommand',
@@ -497,6 +500,7 @@ test(`models that do not support streaming should be handled`, async (t) => {
     })
 
     assertSegments(
+      tx.trace,
       tx.trace.root,
       [
         'Llm/embedding/Bedrock/InvokeModelWithResponseStreamCommand',
diff --git a/test/versioned/aws-sdk-v3/bedrock-embeddings.test.js b/test/versioned/aws-sdk-v3/bedrock-embeddings.test.js
index 830d750aab..7ec4e851ac 100644
--- a/test/versioned/aws-sdk-v3/bedrock-embeddings.test.js
+++ b/test/versioned/aws-sdk-v3/bedrock-embeddings.test.js
@@ -68,6 +68,7 @@ test.afterEach(afterEach)
       assert.equal(response.$metadata.requestId, expected.headers['x-amzn-requestid'])
       assert.deepEqual(body, expected.body)
       assertSegments(
+        tx.trace,
         tx.trace.root,
         ['Llm/embedding/Bedrock/InvokeModelCommand', [expectedExternalPath(modelId)]],
         { exact: false }
@@ -87,17 +88,18 @@ test.afterEach(afterEach)
       const events = agent.customEventAggregator.events.toArray()
       assert.equal(events.length, 1)
       const embedding = events.filter(([{ type }]) => type === 'LlmEmbedding')[0]
+      const [segment] = tx.trace.getChildren(tx.trace.root.id)
       const expectedEmbedding = {
         'id': /[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}/,
         'appName': 'New Relic for Node.js tests',
         'request_id': '743dd35b-744b-4ddf-b5c6-c0f3de2e3142',
         'trace_id': tx.traceId,
-        'span_id': tx.trace.root.children[0].id,
+        'span_id': segment.id,
         'response.model': modelId,
         'vendor': 'bedrock',
         'ingest_source': 'Node',
         'request.model': modelId,
-        'duration': tx.trace.root.children[0].getDurationInMillis(),
+        'duration': segment.getDurationInMillis(),
         'input': prompt,
         'error': false
       }
@@ -157,6 +159,7 @@ test.afterEach(afterEach)
       })
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         ['Llm/embedding/Bedrock/InvokeModelCommand', [expectedExternalPath(modelId)]],
         { exact: false }
@@ -164,17 +167,18 @@ test.afterEach(afterEach)
       const events = agent.customEventAggregator.events.toArray()
       assert.equal(events.length, 1)
       const embedding = events.filter(([{ type }]) => type === 'LlmEmbedding')[0]
+      const [segment] = tx.trace.getChildren(tx.trace.root.id)
       const expectedEmbedding = {
         'id': /[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}/,
         'appName': 'New Relic for Node.js tests',
         'request_id': '743dd35b-744b-4ddf-b5c6-c0f3de2e3142',
         'trace_id': tx.traceId,
-        'span_id': tx.trace.root.children[0].id,
+        'span_id': segment.id,
         'response.model': modelId,
         'vendor': 'bedrock',
         'ingest_source': 'Node',
         'request.model': modelId,
-        'duration': tx.trace.root.children[0].getDurationInMillis(),
+        'duration': segment.getDurationInMillis(),
         'input': prompt,
         'error': true
       }
diff --git a/test/versioned/aws-sdk-v3/client-dynamodb.test.js b/test/versioned/aws-sdk-v3/client-dynamodb.test.js
index c6ead32433..cb77a92f1d 100644
--- a/test/versioned/aws-sdk-v3/client-dynamodb.test.js
+++ b/test/versioned/aws-sdk-v3/client-dynamodb.test.js
@@ -116,7 +116,11 @@ test('DynamoDB', async (t) => {
       }
       tx.end()
       const root = tx.trace.root
-      const segments = common.checkAWSAttributes(root, common.DATASTORE_PATTERN)
+      const segments = common.checkAWSAttributes({ 
+        trace: tx.trace, 
+        segment: root, 
+        pattern: common.DATASTORE_PATTERN
+      })
 
       segments.forEach((segment) => {
         const attrs = segment.attributes.get(common.SEGMENT_DESTINATION)
@@ -186,7 +190,11 @@ function createCommands({ lib, tableName }) {
 
 function finish({ commands, tx, setDatastoreSpy }) {
   const root = tx.trace.root
-  const segments = common.checkAWSAttributes(root, common.DATASTORE_PATTERN)
+  const segments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.DATASTORE_PATTERN
+  })
 
   assert.equal(
     segments.length,
@@ -194,7 +202,11 @@ function finish({ commands, tx, setDatastoreSpy }) {
     `should have ${commands.length} AWS datastore segments`
   )
 
-  const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+  const externalSegments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
   segments.forEach((segment, i) => {
diff --git a/test/versioned/aws-sdk-v3/common.js b/test/versioned/aws-sdk-v3/common.js
index 0a58c1a816..06da50a320 100644
--- a/test/versioned/aws-sdk-v3/common.js
+++ b/test/versioned/aws-sdk-v3/common.js
@@ -17,7 +17,7 @@ const assert = require('node:assert')
 const SEGMENT_DESTINATION = TRANS_SEGMENT
 const helper = require('../../lib/agent_helper')
 
-function checkAWSAttributes(segment, pattern, markedSegments = []) {
+function checkAWSAttributes({ trace, segment, pattern, markedSegments = [] }) {
   const expectedAttrs = {
     'aws.operation': String,
     'aws.service': String,
@@ -30,27 +30,33 @@ function checkAWSAttributes(segment, pattern, markedSegments = []) {
     const attrs = segment.attributes.get(TRANS_SEGMENT)
     match(attrs, expectedAttrs)
   }
-  segment.children.forEach((child) => {
-    checkAWSAttributes(child, pattern, markedSegments)
+  const children = trace.getChildren(segment.id)
+  children.forEach((child) => {
+    checkAWSAttributes({ trace, segment: child, pattern, markedSegments })
   })
 
   return markedSegments
 }
 
-function getMatchingSegments(segment, pattern, markedSegments = []) {
+function getMatchingSegments({ trace, segment, pattern, markedSegments = [] }) {
   if (pattern.test(segment.name)) {
     markedSegments.push(segment)
   }
 
-  segment.children.forEach((child) => {
-    getMatchingSegments(child, pattern, markedSegments)
+  const children = trace.getChildren(segment.id)
+  children.forEach((child) => {
+    getMatchingSegments({ trace, segment: child, pattern, markedSegments })
   })
 
   return markedSegments
 }
 
 function checkExternals({ service, operations, tx, end }) {
-  const externals = checkAWSAttributes(tx.trace.root, EXTERN_PATTERN)
+  const externals = checkAWSAttributes({
+    trace: tx.trace,
+    segment: tx.trace.root,
+    pattern: EXTERN_PATTERN
+  })
   assert.equal(
     externals.length,
     operations.length,
@@ -71,11 +77,12 @@ function checkExternals({ service, operations, tx, end }) {
 }
 
 function assertChatCompletionMessages({ tx, chatMsgs, expectedId, modelId, prompt, resContent }) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const baseMsg = {
     'appName': 'New Relic for Node.js tests',
     'request_id': 'eda0760a-c3f0-4fc1-9a1e-75559d642866',
     'trace_id': tx.traceId,
-    'span_id': tx.trace.root.children[0].id,
+    'span_id': segment.id,
     'response.model': modelId,
     'vendor': 'bedrock',
     'ingest_source': 'Node',
@@ -111,18 +118,19 @@ function assertChatCompletionMessages({ tx, chatMsgs, expectedId, modelId, promp
 }
 
 function assertChatCompletionSummary({ tx, modelId, chatSummary, error = false, numMsgs = 2 }) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const expectedChatSummary = {
     'id': /[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}/,
     'appName': 'New Relic for Node.js tests',
     'request_id': 'eda0760a-c3f0-4fc1-9a1e-75559d642866',
     'llm.conversation_id': 'convo-id',
     'trace_id': tx.traceId,
-    'span_id': tx.trace.root.children[0].id,
+    'span_id': segment.id,
     'response.model': modelId,
     'vendor': 'bedrock',
     'ingest_source': 'Node',
     'request.model': modelId,
-    'duration': tx.trace.root.children[0].getDurationInMillis(),
+    'duration': segment.getDurationInMillis(),
     'response.number_of_messages': error ? 1 : numMsgs,
     'response.choices.finish_reason': error ? undefined : 'endoftext',
     'request.temperature': 0.5,
diff --git a/test/versioned/aws-sdk-v3/lib-dynamodb.test.js b/test/versioned/aws-sdk-v3/lib-dynamodb.test.js
index 032dbc5736..a6f4e091c5 100644
--- a/test/versioned/aws-sdk-v3/lib-dynamodb.test.js
+++ b/test/versioned/aws-sdk-v3/lib-dynamodb.test.js
@@ -136,11 +136,19 @@ test('DynamoDB', async (t) => {
 
 function finish(end, tests, tx) {
   const root = tx.trace.root
-  const segments = common.checkAWSAttributes(root, common.DATASTORE_PATTERN)
+  const segments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.DATASTORE_PATTERN
+  })
 
   assert.equal(segments.length, tests.length, `should have ${tests.length} aws datastore segments`)
 
-  const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+  const externalSegments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
   const accountId = tx.agent.config.cloud.aws.account_id
diff --git a/test/versioned/aws-sdk-v3/sns.test.js b/test/versioned/aws-sdk-v3/sns.test.js
index 9f6b679143..34d35615e4 100644
--- a/test/versioned/aws-sdk-v3/sns.test.js
+++ b/test/versioned/aws-sdk-v3/sns.test.js
@@ -207,11 +207,19 @@ test('SNS', async (t) => {
 function finish(end, tx, destName, setLibrarySpy) {
   const root = tx.trace.root
 
-  const messages = common.checkAWSAttributes(root, common.SNS_PATTERN)
+  const messages = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.SNS_PATTERN
+  })
   assert.equal(messages.length, 1, 'should have 1 message broker segment')
   assert.ok(messages[0].name.endsWith(destName), 'should have appropriate destination')
 
-  const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+  const externalSegments = common.checkAWSAttributes({
+    trace: tx.trace,
+    segment: root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
   const attrs = messages[0].attributes.get(common.SEGMENT_DESTINATION)
diff --git a/test/versioned/aws-sdk-v3/sqs.test.js b/test/versioned/aws-sdk-v3/sqs.test.js
index 8f4b523fab..05471b00ad 100644
--- a/test/versioned/aws-sdk-v3/sqs.test.js
+++ b/test/versioned/aws-sdk-v3/sqs.test.js
@@ -91,7 +91,11 @@ function finish({ transaction, queueName, setLibrarySpy }) {
   const expectedSegmentCount = 3
 
   const root = transaction.trace.root
-  const segments = common.checkAWSAttributes(root, common.SQS_PATTERN)
+  const segments = common.checkAWSAttributes({
+    trace: transaction.trace,
+    segment: root,
+    pattern: common.SQS_PATTERN
+  })
 
   assert.equal(
     segments.length,
@@ -99,7 +103,11 @@ function finish({ transaction, queueName, setLibrarySpy }) {
     `should have ${expectedSegmentCount} AWS MessageBroker/SQS segments`
   )
 
-  const externalSegments = common.checkAWSAttributes(root, common.EXTERN_PATTERN)
+  const externalSegments = common.checkAWSAttributes({
+    trace: transaction.trace,
+    segment: root,
+    pattern: common.EXTERN_PATTERN
+  })
   assert.equal(externalSegments.length, 0, 'should not have any External segments')
 
   const [sendMessage, sendMessageBatch, receiveMessage] = segments
diff --git a/test/versioned/cassandra-driver/query.test.js b/test/versioned/cassandra-driver/query.test.js
index 3022d70fe2..017852c7c2 100644
--- a/test/versioned/cassandra-driver/query.test.js
+++ b/test/versioned/cassandra-driver/query.test.js
@@ -111,11 +111,8 @@ test('executeBatch - callback style', (t, end) => {
         assert.ok(agent.getTransaction(), 'transaction should still be visible')
         assert.equal(value.rows[0][COL], colValArr[0], 'cassandra client should still work')
 
-        assert.equal(
-          transaction.trace.root.children.length,
-          1,
-          'there should be only one child of the root'
-        )
+        const children = transaction.trace.getChildren(transaction.trace.root.id)
+        assert.equal(children.length, 1, 'there should be only one child of the root')
         verifyTrace(agent, transaction.trace, `${KS}.${FAM}`)
         transaction.end()
         checkMetric(agent)
@@ -143,12 +140,8 @@ test('executeBatch - promise style', (t, end) => {
           .then((result) => {
             assert.ok(agent.getTransaction(), 'transaction should still be visible')
             assert.equal(result.rows[0][COL], colValArr[0], 'cassandra client should still work')
-
-            assert.equal(
-              transaction.trace.root.children.length,
-              2,
-              'there should be two children of the root'
-            )
+            const children = transaction.trace.getChildren(transaction.trace.root.id)
+            assert.equal(children.length, 2, 'there should be two children of the root')
             verifyTrace(agent, transaction.trace, `${KS}.${FAM}`)
             transaction.end()
             checkMetric(agent)
@@ -233,6 +226,7 @@ function verifyTrace(agent, trace, table) {
   assert.ok(trace.root, 'root element should exist')
 
   const setSegment = findSegment(
+    trace,
     trace.root,
     'Datastore/statement/Cassandra/' + table + '/insert/batch'
   )
@@ -242,18 +236,22 @@ function verifyTrace(agent, trace, table) {
   if (setSegment) {
     verifyTraceSegment(agent, setSegment, 'insert/batch')
 
+    const children = trace.getChildren(setSegment.id)
     assert.ok(
-      setSegment.children.length >= 2,
+      children.length >= 2,
       'set should have at least a dns lookup and callback/promise child'
     )
-
-    const getSegment = findSegment(trace.root, 'Datastore/statement/Cassandra/' + table + '/select')
+    const getSegment = findSegment(
+      trace,
+      trace.root,
+      'Datastore/statement/Cassandra/' + table + '/select'
+    )
     assert.ok(getSegment, 'trace segment for select should exist')
 
     if (getSegment) {
+      const getChildren = trace.getChildren(getSegment.id)
       verifyTraceSegment(agent, getSegment, 'select')
-
-      assert.ok(getSegment.children.length >= 1, 'get should have a callback/promise segment')
+      assert.ok(getChildren.length >= 1, 'get should have a callback/promise segment')
       assert.ok(getSegment.timer.hrDuration, 'trace segment should have ended')
     }
   }
diff --git a/test/versioned/connect/route.test.js b/test/versioned/connect/route.test.js
index 46c5196b42..c238a809c3 100644
--- a/test/versioned/connect/route.test.js
+++ b/test/versioned/connect/route.test.js
@@ -39,7 +39,7 @@ test('should properly name transaction from route name', async (t) => {
     plan.equal(tx.verb, 'GET', 'HTTP method is GET')
     plan.equal(tx.statusCode, 200, 'status code is OK')
     plan.ok(tx.trace, 'transaction has trace')
-    const web = tx.trace.root.children[0]
+    const [web] = tx.trace.getChildren(tx.trace.root.id)
     plan.ok(web, 'trace has web segment')
     plan.equal(web.name, tx.name, 'segment name and transaction name match')
     plan.equal(web.partialName, 'Connect/GET//foo', 'should have partial name for apdex')
@@ -73,7 +73,7 @@ test('should default to `/` when no route is specified', async (t) => {
     plan.equal(tx.verb, 'GET', 'HTTP method is GET')
     plan.equal(tx.statusCode, 200, 'status code is OK')
     plan.ok(tx.trace, 'transaction has trace')
-    const web = tx.trace.root.children[0]
+    const [web] = tx.trace.getChildren(tx.trace.root.id)
     plan.ok(web, 'trace has web segment')
     plan.equal(web.name, tx.name, 'segment name and transaction name match')
     plan.equal(web.partialName, 'Connect/GET//', 'should have partial name for apdex')
diff --git a/test/versioned/disabled-instrumentation/disabled-express.test.js b/test/versioned/disabled-instrumentation/disabled-express.test.js
index 83763685c4..fd67eeabda 100644
--- a/test/versioned/disabled-instrumentation/disabled-express.test.js
+++ b/test/versioned/disabled-instrumentation/disabled-express.test.js
@@ -36,7 +36,7 @@ test('should still record child segments if express instrumentation is disabled'
       assert.equal(tx.name, 'WebTransaction/NormalizedUri/*', 'should not name transactions')
       const rootSegment = tx.trace.root
       const expectedSegments = ['WebTransaction/NormalizedUri/*', ['Datastore/operation/Redis/get']]
-      assertSegments(rootSegment, expectedSegments)
+      assertSegments(tx.trace, rootSegment, expectedSegments)
       resolve()
     })
   })
diff --git a/test/versioned/disabled-instrumentation/disabled-ioredis.test.js b/test/versioned/disabled-instrumentation/disabled-ioredis.test.js
index c433d08789..684e217ce6 100644
--- a/test/versioned/disabled-instrumentation/disabled-ioredis.test.js
+++ b/test/versioned/disabled-instrumentation/disabled-ioredis.test.js
@@ -48,7 +48,7 @@ test('Disabled PG scenarios', async (t) => {
       await collection.countDocuments()
       await redisClient.get('bar')
       tx.end()
-      assertSegments(tx.trace.root, [
+      assertSegments(tx.trace, tx.trace.root, [
         'Datastore/statement/MongoDB/disabled-inst-test/aggregate',
         'Datastore/statement/MongoDB/disabled-inst-test/next'
       ])
@@ -65,7 +65,7 @@ test('Disabled PG scenarios', async (t) => {
           redisClient.get('bar', (innerErr) => {
             tx.end()
             assert.equal(innerErr, null)
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'Datastore/statement/MongoDB/disabled-inst-test/aggregate',
               'Datastore/statement/MongoDB/disabled-inst-test/next'
             ])
diff --git a/test/versioned/elastic/elasticsearch.test.js b/test/versioned/elastic/elasticsearch.test.js
index 02253b0aa1..580ffffbc1 100644
--- a/test/versioned/elastic/elasticsearch.test.js
+++ b/test/versioned/elastic/elasticsearch.test.js
@@ -103,8 +103,7 @@ test('Elasticsearch instrumentation', async (t) => {
       assert.ok(transaction, 'transaction should be visible')
       await client.indices.create({ index })
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      const firstChild = trace.root.children[0]
+      const [firstChild] = trace.getChildren(trace.root.id)
       assert.equal(
         firstChild.name,
         `Datastore/statement/ElasticSearch/${index}/index.create`,
@@ -119,8 +118,7 @@ test('Elasticsearch instrumentation', async (t) => {
       await bulkInsert({ client, pkgVersion })
       assert.ok(transaction, 'transaction should still be visible after bulk create')
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      const firstChild = trace.root.children[0]
+      const [firstChild] = trace.getChildren(trace.root.id)
       assert.equal(
         firstChild.name,
         'Datastore/statement/ElasticSearch/any/bulk.create',
@@ -144,10 +142,9 @@ test('Elasticsearch instrumentation', async (t) => {
       })
       assert.ok(transaction, 'transaction should still be visible after bulk create')
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      assert.ok(trace?.root?.children?.[1], 'trace, trace root, and second child should exist')
       // helper interface results in a first child of timers.setTimeout, with the second child related to the operation
-      const secondChild = trace.root.children[1]
+      const [firstChild, secondChild] = trace.getChildren(trace.root.id)
+      assert.ok(firstChild)
       assert.equal(
         secondChild.name,
         'Datastore/statement/ElasticSearch/any/bulk.create',
@@ -168,8 +165,7 @@ test('Elasticsearch instrumentation', async (t) => {
       assert.ok(search, 'search should return a result')
       assert.ok(transaction, 'transaction should still be visible after search')
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      const firstChild = trace.root.children[0]
+      const [firstChild] = trace.getChildren(trace.root.id)
       assert.equal(
         firstChild.name,
         `Datastore/statement/ElasticSearch/${DB_INDEX_2}/search`,
@@ -204,8 +200,7 @@ test('Elasticsearch instrumentation', async (t) => {
       assert.ok(search, 'search should return a result')
       assert.ok(transaction, 'transaction should still be visible after search')
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      const firstChild = trace.root.children[0]
+      const [firstChild] = trace.getChildren(trace.root.id)
       assert.equal(
         firstChild.name,
         `Datastore/statement/ElasticSearch/${DB_INDEX}/search`,
@@ -243,8 +238,7 @@ test('Elasticsearch instrumentation', async (t) => {
       assert.ok(search, 'search should return a result')
       assert.ok(transaction, 'transaction should still be visible after search')
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      const firstChild = trace.root.children[0]
+      const [firstChild] = trace.getChildren(trace.root.id)
       assert.equal(
         firstChild.name,
         'Datastore/statement/ElasticSearch/any/search',
@@ -292,8 +286,7 @@ test('Elasticsearch instrumentation', async (t) => {
       assert.equal(results?.[1]?.hits?.hits?.length, 8, 'second search should return ten results')
       assert.ok(transaction, 'transaction should still be visible after search')
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      const firstChild = trace.root.children[0]
+      const [firstChild] = trace.getChildren(trace.root.id)
       assert.equal(
         firstChild.name,
         'Datastore/statement/ElasticSearch/any/msearch.create',
@@ -334,8 +327,7 @@ test('Elasticsearch instrumentation', async (t) => {
       assert.equal(resultsB?.hits?.length, 8, 'second search should return ten results')
       assert.ok(transaction, 'transaction should still be visible after search')
       const trace = transaction.trace
-      assert.ok(trace?.root?.children?.[0], 'trace, trace root, and first child should exist')
-      const firstChild = trace.root.children[0]
+      const [firstChild] = trace.getChildren(trace.root.id)
       assert.equal(
         firstChild.name,
         'timers.setTimeout',
@@ -422,7 +414,7 @@ test('Elasticsearch instrumentation', async (t) => {
         ...documentProp
       })
 
-      const createSegment = transaction.trace.root.children[0]
+      const [createSegment] = transaction.trace.getChildren(transaction.trace.root.id)
       const attributes = createSegment.getAttributes()
       assert.equal(attributes.host, undefined, 'should not have host attribute')
       assert.equal(attributes.port_path_or_id, undefined, 'should not have port attribute')
@@ -445,7 +437,7 @@ test('Elasticsearch instrumentation', async (t) => {
       } catch (e) {
         assert.ok(e, 'should not be able to create an index named _search')
       }
-      const firstChild = transaction?.trace?.root?.children[0]
+      const [firstChild] = transaction.trace.getChildren(transaction.trace.root.id)
       assert.equal(
         firstChild.name,
         'Datastore/statement/ElasticSearch/_search/index.create',
diff --git a/test/versioned/elastic/elasticsearchNoop.test.js b/test/versioned/elastic/elasticsearchNoop.test.js
index 1ffd758f6b..c31bfb0686 100644
--- a/test/versioned/elastic/elasticsearchNoop.test.js
+++ b/test/versioned/elastic/elasticsearchNoop.test.js
@@ -46,7 +46,7 @@ test('Elasticsearch instrumentation', async (t) => {
         } catch (e) {
           assert.ok(!e, 'should not error')
         }
-        const firstChild = transaction?.trace?.root?.children[0]
+        const [firstChild] = transaction.trace.getChildren(transaction.trace.root.id)
         assert.equal(
           firstChild.name,
           `External/localhost:9200/${DB_INDEX}`,
diff --git a/test/versioned/express-esm/segments.test.mjs b/test/versioned/express-esm/segments.test.mjs
index b85e5feadd..0f948b51d4 100644
--- a/test/versioned/express-esm/segments.test.mjs
+++ b/test/versioned/express-esm/segments.test.mjs
@@ -58,6 +58,7 @@ test('first two segments are built-in Express middleware', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server })
   assertSegments(
+    transaction.trace,
     rootSegment,
     ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']],
     assertSegmentsOptions
@@ -87,6 +88,7 @@ test('segments for route handler', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server })
   assertSegments(
+    transaction.trace,
     rootSegment,
     ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']],
     assertSegmentsOptions
@@ -104,6 +106,7 @@ test('route function names are in segment names', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server })
   assertSegments(
+    transaction.trace,
     rootSegment,
     ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
     assertSegmentsOptions
@@ -121,6 +124,7 @@ test('middleware mounted on a path should produce correct names', async (t) => {
 
   const { transaction } = await runTest({ agent, server, endpoint: '/test/1' })
   const routeSegment = findSegment(
+    transaction.trace,
     transaction.trace.root,
     NAMES.EXPRESS.MIDDLEWARE + 'handler//test/:id'
   )
@@ -144,6 +148,7 @@ test('each handler in route has its own segment', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Route Path: /test',
@@ -170,6 +175,7 @@ test('segments for routers', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint: '/router1/test' })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /router1',
@@ -202,6 +208,7 @@ test('two root routers', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /',
@@ -238,6 +245,7 @@ test('router mounted as a route handler', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       `Expressjs/Route Path: ${segmentPath}`,
@@ -268,6 +276,7 @@ test('segments for routers', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint: '/router1/test' })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /router1',
@@ -300,6 +309,7 @@ test('segments for sub-app', async (t) => {
     : 'Expressjs/Mounted App: /subapp1'
 
   assertSegments(
+    transaction.trace,
     rootSegment,
     [firstSegment, ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']]],
     assertSegmentsOptions
@@ -338,6 +348,7 @@ test('segments for sub-app router', async (t) => {
     : 'Expressjs/Mounted App: /subapp1'
 
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       firstSegment,
@@ -375,6 +386,7 @@ test('segments for wildcard', async (t) => {
     : 'Expressjs/Mounted App: /subapp1'
 
   assertSegments(
+    transaction.trace,
     rootSegment,
     [firstSegment, ['Expressjs/Route Path: /:app', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']]],
     assertSegmentsOptions
@@ -409,6 +421,7 @@ test('router with subapp', async (t) => {
     : 'Expressjs/Mounted App: /subapp1'
 
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /router1',
@@ -432,7 +445,12 @@ test('mounted middleware', async (t) => {
   })
 
   const { rootSegment, transaction } = await runTest({ agent, server })
-  assertSegments(rootSegment, [NAMES.EXPRESS.MIDDLEWARE + 'myHandler//test'], assertSegmentsOptions)
+  assertSegments(
+    transaction.trace,
+    rootSegment,
+    [NAMES.EXPRESS.MIDDLEWARE + 'myHandler//test'],
+    assertSegmentsOptions
+  )
 
   checkMetrics(transaction.metrics, [NAMES.EXPRESS.MIDDLEWARE + 'myHandler//test'])
 })
@@ -450,6 +468,7 @@ test('error middleware', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Route Path: /test',
@@ -488,6 +507,7 @@ test('error handler in router', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /router',
@@ -531,6 +551,7 @@ test('error handler in second router', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /router1',
@@ -574,6 +595,7 @@ test('error handler outside of router', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /router',
@@ -614,6 +636,7 @@ test('error handler outside of two routers', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [
       'Expressjs/Router: /router1',
@@ -645,6 +668,7 @@ test('when using a route variable', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint: '/a/b' })
   assertSegments(
+    transaction.trace,
     rootSegment,
     ['Expressjs/Route Path: /:foo/:bar', [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
     assertSegmentsOptions
@@ -667,6 +691,7 @@ test('when using a string pattern in path', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint: '/abcd' })
   assertSegments(
+    transaction.trace,
     rootSegment,
     [`Expressjs/Route Path: ${path}`, [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
     assertSegmentsOptions
@@ -684,6 +709,7 @@ test('when using a regular expression in path', async (t) => {
 
   const { rootSegment, transaction } = await runTest({ agent, server, endpoint: '/a' })
   assertSegments(
+    transaction.trace,
     rootSegment,
     ['Expressjs/Route Path: /a/', [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
     assertSegmentsOptions
@@ -694,7 +720,7 @@ test('when using a regular expression in path', async (t) => {
 
 async function runTest({ agent, server, endpoint = '/test', errors = 0 }) {
   const transaction = await makeRequestAndFinishTransaction({ server, agent, endpoint })
-  const rootSegment = transaction.trace.root.children[0]
+  const [rootSegment] = transaction.trace.getChildren(transaction.trace.root.id)
 
   assert.equal(agent.errors.traceAggregator.errors.length, errors, `should have ${errors} errors`)
   return { rootSegment, transaction }
diff --git a/test/versioned/express-esm/transaction-naming.test.mjs b/test/versioned/express-esm/transaction-naming.test.mjs
index 8d94db031b..3e6363830a 100644
--- a/test/versioned/express-esm/transaction-naming.test.mjs
+++ b/test/versioned/express-esm/transaction-naming.test.mjs
@@ -401,7 +401,8 @@ test('Express transaction names are unaffected by errorware', async (t) => {
   const promise = new Promise((resolve) => {
     transactionHandler = function (tx) {
       const expected = 'WebTransaction/Expressjs/GET//test'
-      plan.equal(tx.trace.root.children[0].name, expected)
+      const [baseSegment] = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(baseSegment.name, expected)
       resolve()
     }
   })
diff --git a/test/versioned/express/async-handlers.test.js b/test/versioned/express/async-handlers.test.js
index f350d38a19..f2bba1fa6f 100644
--- a/test/versioned/express/async-handlers.test.js
+++ b/test/versioned/express/async-handlers.test.js
@@ -36,8 +36,8 @@ test('async handlers', { skip: !isExpress5() }, async (t) => {
     })
 
     const tx = await runTest(t, '/test')
-    const [children] = tx.trace.root.children
-    const [mw, handler] = children.children
+    const [child] = tx.trace.getChildren(tx.trace.root.id)
+    const [mw, handler] = tx.trace.getChildren(child.id)
     assert.ok(
       Math.ceil(mw.getDurationInMillis()) >= mwTimeout,
       `should be at least ${mwTimeout} for middleware segment`
diff --git a/test/versioned/express/bare-router.test.js b/test/versioned/express/bare-router.test.js
index 6620487144..1f0789dad8 100644
--- a/test/versioned/express/bare-router.test.js
+++ b/test/versioned/express/bare-router.test.js
@@ -35,7 +35,7 @@ test('Express router introspection', async function (t) {
     plan.equal(transaction.verb, 'GET', 'HTTP method is GET')
     plan.ok(transaction.trace, 'transaction has trace')
 
-    const web = transaction.trace.root.children[0]
+    const [web] = transaction.trace.getChildren(transaction.trace.root.id)
     plan.ok(web, 'trace has web segment')
     plan.equal(web.name, transaction.name, 'segment name and transaction name match')
 
diff --git a/test/versioned/express/client-disconnect.test.js b/test/versioned/express/client-disconnect.test.js
index 8505abe891..94fadf510f 100644
--- a/test/versioned/express/client-disconnect.test.js
+++ b/test/versioned/express/client-disconnect.test.js
@@ -44,6 +44,7 @@ test('Client Premature Disconnection', { timeout: 3000 }, (t, end) => {
 
   agent.on('transactionFinished', (transaction) => {
     assertSegments(
+      transaction.trace,
       transaction.trace.root,
       [
         'WebTransaction/Expressjs/POST//test',
diff --git a/test/versioned/express/router-params.test.js b/test/versioned/express/router-params.test.js
index 775fa507a2..8a0d0403b1 100644
--- a/test/versioned/express/router-params.test.js
+++ b/test/versioned/express/router-params.test.js
@@ -46,7 +46,7 @@ test('Express router introspection', async function (t) {
     plan.equal(transaction.verb, 'GET', 'HTTP method is GET')
     plan.ok(transaction.trace, 'transaction has trace')
 
-    const web = transaction.trace.root.children[0]
+    const [web] = transaction.trace.getChildren(transaction.trace.root.id)
     plan.ok(web, 'trace has web segment')
     plan.equal(web.name, transaction.name, 'segment name and transaction name match')
     plan.equal(
diff --git a/test/versioned/express/segments.test.js b/test/versioned/express/segments.test.js
index 8ebef17990..a2f48872ad 100644
--- a/test/versioned/express/segments.test.js
+++ b/test/versioned/express/segments.test.js
@@ -36,10 +36,11 @@ test('first two segments are built-in Express middlewares', function (t, end) {
     res.end()
   })
 
-  runTest(t, function (segments, transaction) {
+  runTest(t, function (root, transaction) {
     // TODO: check for different HTTP methods
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']],
       assertSegmentsOptions
     )
@@ -59,7 +60,7 @@ test('middleware with child segment gets named correctly', function (t, end) {
     }, 1)
   })
 
-  runTest(t, function (segments, transaction) {
+  runTest(t, function (root, transaction) {
     checkMetrics(transaction.metrics, [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>//test'])
 
     end()
@@ -73,9 +74,10 @@ test('segments for route handler', function (t, end) {
     res.end()
   })
 
-  runTest(t, function (segments, transaction) {
+  runTest(t, function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']],
       assertSegmentsOptions
     )
@@ -93,9 +95,10 @@ test('route function names are in segment names', function (t, end) {
     res.end()
   })
 
-  runTest(t, function (segments, transaction) {
+  runTest(t, function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
       assertSegmentsOptions
     )
@@ -113,9 +116,10 @@ test('middleware mounted on a path should produce correct names', function (t, e
     res.send()
   })
 
-  runTest(t, '/test/1', function (segments, transaction) {
+  runTest(t, '/test/1', function (root, transaction) {
     const segment = findSegment(
-      transaction.trace.root,
+      transaction.trace,
+      root,
       NAMES.EXPRESS.MIDDLEWARE + 'handler//test/:id'
     )
     assert.ok(segment)
@@ -139,9 +143,10 @@ test('each handler in route has its own segment', function (t, end) {
     }
   )
 
-  runTest(t, function (segments, transaction) {
+  runTest(t, function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         'Expressjs/Route Path: /test',
         [NAMES.EXPRESS.MIDDLEWARE + 'handler1', NAMES.EXPRESS.MIDDLEWARE + 'handler2']
@@ -168,9 +173,10 @@ test('segments for routers', function (t, end) {
 
   app.use('/router1', router)
 
-  runTest(t, '/router1/test', function (segments, transaction) {
+  runTest(t, '/router1/test', function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         'Expressjs/Router: /router1',
         ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']]
@@ -203,9 +209,10 @@ test('two root routers', function (t, end) {
   })
   app.use('/', router2)
 
-  runTest(t, '/test', function (segments, transaction) {
+  runTest(t, '/test', function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         'Expressjs/Router: /',
         'Expressjs/Router: /',
@@ -241,9 +248,10 @@ test('router mounted as a route handler', function (t, end) {
   }
   app.get(path, router1)
 
-  runTest(t, '/test', function (segments, transaction) {
+  runTest(t, '/test', function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         `Expressjs/Route Path: ${segmentPath}`,
         [
@@ -274,9 +282,10 @@ test('segments for routers', function (t, end) {
 
   app.use('/router1', router)
 
-  runTest(t, '/router1/test', function (segments, transaction) {
+  runTest(t, '/router1/test', function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         'Expressjs/Router: /router1',
         ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']]
@@ -304,14 +313,15 @@ test('segments for sub-app', function (t, end) {
 
   app.use('/subapp1', subapp)
 
-  runTest(t, '/subapp1/test', function (segments, transaction) {
+  runTest(t, '/subapp1/test', function (root, transaction) {
     // express 5 no longer handles child routers as mounted applications
     const firstSegment = isExpress5
       ? NAMES.EXPRESS.MIDDLEWARE + 'app//subapp1'
       : 'Expressjs/Mounted App: /subapp1'
 
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [firstSegment, ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']]],
       assertSegmentsOptions
     )
@@ -345,13 +355,14 @@ test('segments for sub-app router', function (t, end) {
 
   app.use('/subapp1', subapp)
 
-  runTest(t, '/subapp1/test', function (segments, transaction) {
+  runTest(t, '/subapp1/test', function (root, transaction) {
     // express 5 no longer handles child routers as mounted applications
     const firstSegment = isExpress5
       ? NAMES.EXPRESS.MIDDLEWARE + 'app//subapp1'
       : 'Expressjs/Mounted App: /subapp1'
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         firstSegment,
         [
@@ -384,13 +395,14 @@ test('segments for wildcard', function (t, end) {
 
   app.use('/subapp1', subapp)
 
-  runTest(t, '/subapp1/test', function (segments, transaction) {
+  runTest(t, '/subapp1/test', function (root, transaction) {
     // express 5 no longer handles child routers as mounted applications
     const firstSegment = isExpress5
       ? NAMES.EXPRESS.MIDDLEWARE + 'app//subapp1'
       : 'Expressjs/Mounted App: /subapp1'
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [firstSegment, ['Expressjs/Route Path: /:app', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']]],
       assertSegmentsOptions
     )
@@ -416,13 +428,14 @@ test('router with subapp', function (t, end) {
   router.use('/subapp1', subapp)
   app.use('/router1', router)
 
-  runTest(t, '/router1/subapp1/test', function (segments, transaction) {
+  runTest(t, '/router1/subapp1/test', function (root, transaction) {
     // express 5 no longer handles child routers as mounted applications
     const subAppSegment = isExpress5
       ? NAMES.EXPRESS.MIDDLEWARE + 'app//subapp1'
       : 'Expressjs/Mounted App: /subapp1'
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         'Expressjs/Router: /router1',
         [subAppSegment, ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']]]
@@ -447,9 +460,10 @@ test('mounted middleware', function (t, end) {
     res.end()
   })
 
-  runTest(t, function (segments, transaction) {
+  runTest(t, function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [NAMES.EXPRESS.MIDDLEWARE + 'myHandler//test'],
       assertSegmentsOptions
     )
@@ -471,9 +485,10 @@ test('error middleware', function (t, end) {
     res.end()
   })
 
-  runTest(t, function (segments, transaction) {
+  runTest(t, function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       [
         'Expressjs/Route Path: /test',
         [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>'],
@@ -515,12 +530,13 @@ test('error handler in router', function (t, end) {
   runTest(
     t,
     {
-      endpoint: endpoint,
+      endpoint,
       errors: 0
     },
-    function (segments, transaction) {
+    function (root, transaction) {
       assertSegments(
-        transaction.trace.root.children[0],
+        transaction.trace,
+        root,
         [
           'Expressjs/Router: /router',
           [
@@ -571,9 +587,10 @@ test('error handler in second router', function (t, end) {
       endpoint: endpoint,
       errors: 0
     },
-    function (segments, transaction) {
+    function (root, transaction) {
       assertSegments(
-        transaction.trace.root.children[0],
+        transaction.trace,
+        root,
         [
           'Expressjs/Router: /router1',
           [
@@ -624,9 +641,10 @@ test('error handler outside of router', function (t, end) {
       endpoint: endpoint,
       errors: 0
     },
-    function (segments, transaction) {
+    function (root, transaction) {
       assertSegments(
-        transaction.trace.root.children[0],
+        transaction.trace,
+        root,
         [
           'Expressjs/Router: /router',
           ['Expressjs/Route Path: /test', [NAMES.EXPRESS.MIDDLEWARE + '<anonymous>']],
@@ -674,9 +692,10 @@ test('error handler outside of two routers', function (t, end) {
       endpoint: endpoint,
       errors: 0
     },
-    function (segments, transaction) {
+    function (root, transaction) {
       assertSegments(
-        transaction.trace.root.children[0],
+        transaction.trace,
+        root,
         [
           'Expressjs/Router: /router1',
           [
@@ -709,9 +728,10 @@ test('when using a route variable', function (t, end) {
     res.end()
   })
 
-  runTest(t, '/a/b', function (segments, transaction) {
+  runTest(t, '/a/b', function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       ['Expressjs/Route Path: /:foo/:bar', [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
       assertSegmentsOptions
     )
@@ -734,9 +754,10 @@ test('when using a string pattern in path', function (t, end) {
     res.end()
   })
 
-  runTest(t, '/abcd', function (segments, transaction) {
+  runTest(t, '/abcd', function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       ['Expressjs/Route Path: ' + path, [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
       assertSegmentsOptions
     )
@@ -754,9 +775,10 @@ test('when using a regular expression in path', function (t, end) {
     res.end()
   })
 
-  runTest(t, '/a', function (segments, transaction) {
+  runTest(t, '/a', function (root, transaction) {
     assertSegments(
-      transaction.trace.root.children[0],
+      transaction.trace,
+      root,
       ['Expressjs/Route Path: /a/', [NAMES.EXPRESS.MIDDLEWARE + 'myHandler']],
       assertSegmentsOptions
     )
@@ -785,9 +807,15 @@ for (const enabled of codeLevelMetrics) {
       res.end()
     })
 
-    runTest(t, '/chained', function (segments, transaction) {
-      const routeSegment = findSegment(transaction.trace.root, 'Expressjs/Route Path: /chained')
-      const [mw1Segment, mw2Segment, handlerSegment] = routeSegment.children
+    runTest(t, '/chained', function (root, transaction) {
+      const routeSegment = findSegment(
+        transaction.trace,
+        transaction.trace.root,
+        'Expressjs/Route Path: /chained'
+      )
+      const [mw1Segment, mw2Segment, handlerSegment] = transaction.trace.getChildren(
+        routeSegment.id
+      )
       const defaultPath = 'test/versioned/express/segments.test.js'
       assertCLMAttrs({
         segments: [
@@ -832,11 +860,11 @@ function runTest(t, options, callback) {
   }
 
   agent.on('transactionFinished', function (tx) {
-    const baseSegment = tx.trace.root.children[0]
+    const [baseSegment] = tx.trace.getChildren(tx.trace.root.id)
 
     assert.equal(agent.errors.traceAggregator.errors.length, errors, 'should have errors')
 
-    callback(baseSegment.children, tx)
+    callback(baseSegment, tx)
   })
 
   makeRequest(port, endpoint, function (response) {
diff --git a/test/versioned/express/transaction-naming.test.js b/test/versioned/express/transaction-naming.test.js
index eca3baf371..c64a3d4437 100644
--- a/test/versioned/express/transaction-naming.test.js
+++ b/test/versioned/express/transaction-naming.test.js
@@ -379,7 +379,8 @@ test('Express transaction names are unaffected by errorware', async function (t)
 
   agent.on('transactionFinished', function (tx) {
     const expected = 'WebTransaction/Expressjs/GET//test'
-    plan.equal(tx.trace.root.children[0].name, expected)
+    const [baseSegment] = tx.trace.getChildren(tx.trace.root.id)
+    plan.equal(baseSegment.name, expected)
   })
 
   app.use('/test', function () {
diff --git a/test/versioned/fastify/add-hook.test.js b/test/versioned/fastify/add-hook.test.js
index cecf24da28..1dccf563fd 100644
--- a/test/versioned/fastify/add-hook.test.js
+++ b/test/versioned/fastify/add-hook.test.js
@@ -108,7 +108,7 @@ test('non-error hooks', async (t) => {
         ]
       ]
     }
-    assertSegments(transaction.trace.root, expectedSegments)
+    assertSegments(transaction.trace, transaction.trace.root, expectedSegments)
 
     txPassed = true
   })
@@ -168,7 +168,7 @@ test('error hook', async function errorHookTest(t) {
       ]
     }
 
-    assertSegments(transaction.trace.root, expectedSegments)
+    assertSegments(transaction.trace, transaction.trace.root, expectedSegments)
 
     txPassed = true
   })
diff --git a/test/versioned/fastify/code-level-metrics-hooks.test.js b/test/versioned/fastify/code-level-metrics-hooks.test.js
index 31ad174960..cafcf13d00 100644
--- a/test/versioned/fastify/code-level-metrics-hooks.test.js
+++ b/test/versioned/fastify/code-level-metrics-hooks.test.js
@@ -43,11 +43,12 @@ async function performTest(t) {
 
   let txPassed = false
   agent.on('transactionFinished', (transaction) => {
-    const baseSegment = transaction.trace.root.children
-    const [onRequestSegment, handlerSegment] = helper.isSecurityAgentEnabled(agent)
-      ? baseSegment[0].children[0].children
-      : baseSegment[0].children
-    const onSendSegment = handlerSegment.children[0]
+    const [baseSegment] = transaction.trace.getChildren(transaction.trace.root.id)
+    let [onRequestSegment, handlerSegment] = transaction.trace.getChildren(baseSegment.id)
+    if (helper.isSecurityAgentEnabled(agent)) {
+      ;[onRequestSegment, handlerSegment] = transaction.trace.getChildren(onRequestSegment.id)
+    }
+    const [onSendSegment] = transaction.trace.getChildren(handlerSegment.id)
     assertCLMAttrs({
       segments: [
         {
diff --git a/test/versioned/fastify/code-level-metrics-middleware.test.js b/test/versioned/fastify/code-level-metrics-middleware.test.js
index 0449d88310..b112ce2c2a 100644
--- a/test/versioned/fastify/code-level-metrics-middleware.test.js
+++ b/test/versioned/fastify/code-level-metrics-middleware.test.js
@@ -53,9 +53,12 @@ async function setup(t, config) {
   }
 }
 
-function assertSegments(testContext, baseSegment, isCLMEnabled) {
-  const { agent } = testContext.nr
-  const { children } = helper.isSecurityAgentEnabled(agent) ? baseSegment.children[0] : baseSegment
+function assertSegments({ t, trace, baseSegment, isCLMEnabled }) {
+  const { agent } = t.nr
+  let children = trace.getChildren(baseSegment.id)
+  if (helper.isSecurityAgentEnabled(agent)) {
+    children = trace.getChildren(children[0].id)
+  }
   // TODO: once we drop v2 support, this function can be removed and assert inline in test below
   if (semver.satisfies(pkgVersion, '>=3')) {
     const [middieSegment, handlerSegment] = children
@@ -105,7 +108,13 @@ async function performTest(t) {
 
   agent.on('transactionFinished', (transaction) => {
     calls.test++
-    assertSegments(t, transaction.trace.root.children[0], agent.config.code_level_metrics.enabled)
+    const [baseSegment] = transaction.trace.getChildren(transaction.trace.root.id)
+    assertSegments({
+      t,
+      baseSegment,
+      trace: transaction.trace,
+      isCLMEnabled: agent.config.code_level_metrics.enabled
+    })
   })
 
   await fastify.listen({ port: 0 })
diff --git a/test/versioned/fastify/naming-common.js b/test/versioned/fastify/naming-common.js
index d7ef4a51ad..efb21e6da7 100644
--- a/test/versioned/fastify/naming-common.js
+++ b/test/versioned/fastify/naming-common.js
@@ -43,7 +43,7 @@ module.exports = async function runTests(t, getExpectedSegments) {
           ]
         }
 
-        assertSegments(transaction.trace.root, expectedSegments)
+        assertSegments(transaction.trace, transaction.trace.root, expectedSegments)
       })
 
       await fastify.listen({ port: 0 })
diff --git a/test/versioned/grpc/util.cjs b/test/versioned/grpc/util.cjs
index 2ae0864095..4eadf1e04d 100644
--- a/test/versioned/grpc/util.cjs
+++ b/test/versioned/grpc/util.cjs
@@ -142,8 +142,8 @@ util.assertExternalSegment = function assertExternalSegment(
 ) {
   const methodName = util.getRPCName(fnName)
   const segmentName = `${EXTERNAL.PREFIX}${CLIENT_ADDR}:${port}${methodName}`
-  assertSegments(tx.trace.root, [segmentName], { exact: false }, { assert })
-  const segment = metricsHelpers.findSegment(tx.trace.root, segmentName)
+  assertSegments(tx.trace, tx.trace.root, [segmentName], { exact: false }, { assert })
+  const segment = metricsHelpers.findSegment(tx.trace, tx.trace.root, segmentName)
   const attributes = segment.getAttributes()
   assert.equal(
     attributes.url,
diff --git a/test/versioned/hapi/render.test.js b/test/versioned/hapi/render.test.js
index cbe3984c43..edfebaa396 100644
--- a/test/versioned/hapi/render.test.js
+++ b/test/versioned/hapi/render.test.js
@@ -103,13 +103,14 @@ test('using EJS templates', { timeout: 2000 }, (t, end) => {
   })
 
   function verifyEnded(root, tx) {
-    for (let i = 0, len = root.children.length; i < len; i++) {
-      const segment = root.children[i]
+    const children = tx.trace.getChildren(root.id)
+    for (let i = 0, len = children.length; i < len; i++) {
+      const segment = children[i]
       assert.ok(
         segment.timer.hasEnd(),
         util.format('verify %s (%s) has ended', segment.name, tx.id)
       )
-      if (segment.children) {
+      if (tx.trace.getChildren(segment.id)) {
         verifyEnded(segment, tx)
       }
     }
diff --git a/test/versioned/hapi/router.test.js b/test/versioned/hapi/router.test.js
index 2e23fcacc9..7b4682b17c 100644
--- a/test/versioned/hapi/router.test.js
+++ b/test/versioned/hapi/router.test.js
@@ -42,7 +42,7 @@ function verifier(verb = 'GET') {
     assert.equal(transaction.verb, verb, 'HTTP method is ' + verb)
     assert.ok(transaction.trace, 'transaction has trace')
 
-    const web = transaction.trace.root.children[0]
+    const [web] = transaction.trace.getChildren(transaction.trace.root.id)
     assert.ok(web, 'trace has web segment')
     assert.equal(web.name, transaction.name, 'segment name and transaction name match')
 
@@ -269,8 +269,9 @@ test('using custom handler defaults', (t, end) => {
 test('404 transaction is named correctly', (t, end) => {
   const { agent, server } = t.nr
   agent.on('transactionFinished', function (tx) {
+    const [segment] = tx.trace.getChildren(tx.trace.root.id)
     assert.equal(
-      tx.trace.root.children[0].name,
+      segment.name,
       'WebTransaction/Nodejs/GET/(not found)',
       '404 segment has standardized name'
     )
diff --git a/test/versioned/hapi/segments.test.js b/test/versioned/hapi/segments.test.js
index e16468491e..7859597c5c 100644
--- a/test/versioned/hapi/segments.test.js
+++ b/test/versioned/hapi/segments.test.js
@@ -30,8 +30,8 @@ test.afterEach((ctx) => {
 
 function runTest(agent, server, callback) {
   agent.on('transactionFinished', function (tx) {
-    const baseSegment = tx.trace.root.children[0]
-    callback(baseSegment.children, tx)
+    const [ baseSegment ] = tx.trace.getChildren(tx.trace.root.id)
+    callback(baseSegment, tx)
   })
 
   server.start().then(function () {
@@ -78,9 +78,9 @@ test('route handler is recorded as middleware', (t, end) => {
     }
   })
 
-  runTest(agent, server, function (segments, transaction) {
+  runTest(agent, server, function (baseSegment, transaction) {
     checkMetrics(transaction.metrics, [NAMES.HAPI.MIDDLEWARE + 'myHandler//test'])
-    assertSegments(transaction.trace.root.children[0], [NAMES.HAPI.MIDDLEWARE + 'myHandler//test'])
+    assertSegments(transaction.trace, baseSegment, [NAMES.HAPI.MIDDLEWARE + 'myHandler//test'])
     end()
   })
 })
@@ -100,9 +100,9 @@ test('custom handler type is recorded as middleware', (t, end) => {
     handler: { customHandler: { key1: 'val1' } }
   })
 
-  runTest(agent, server, function (segments, transaction) {
+  runTest(agent, server, function (baseSegment, transaction) {
     checkMetrics(transaction.metrics, [NAMES.HAPI.MIDDLEWARE + 'customHandler//test'])
-    assertSegments(transaction.trace.root.children[0], [
+    assertSegments(transaction.trace, baseSegment, [
       NAMES.HAPI.MIDDLEWARE + 'customHandler//test'
     ])
     end()
@@ -124,12 +124,12 @@ test('extensions are recorded as middleware', (t, end) => {
     }
   })
 
-  runTest(agent, server, function (segments, transaction) {
+  runTest(agent, server, function (baseSegment, transaction) {
     checkMetrics(transaction.metrics, [
       NAMES.HAPI.MIDDLEWARE + '<anonymous>//onRequest',
       NAMES.HAPI.MIDDLEWARE + 'myHandler//test'
     ])
-    assertSegments(transaction.trace.root.children[0], [
+    assertSegments(transaction.trace, baseSegment, [
       NAMES.HAPI.MIDDLEWARE + '<anonymous>//onRequest',
       NAMES.HAPI.MIDDLEWARE + 'myHandler//test'
     ])
@@ -156,12 +156,12 @@ test('custom route handler and extension recorded as middleware', (t, end) => {
     handler: { customHandler: { key1: 'val1' } }
   })
 
-  runTest(agent, server, function (segments, transaction) {
+  runTest(agent, server, function (baseSegment, transaction) {
     checkMetrics(transaction.metrics, [
       NAMES.HAPI.MIDDLEWARE + '<anonymous>//onRequest',
       NAMES.HAPI.MIDDLEWARE + 'customHandler//test'
     ])
-    assertSegments(transaction.trace.root.children[0], [
+    assertSegments(transaction.trace, baseSegment, [
       NAMES.HAPI.MIDDLEWARE + '<anonymous>//onRequest',
       NAMES.HAPI.MIDDLEWARE + 'customHandler//test'
     ])
@@ -191,8 +191,8 @@ for (const clmEnabled of [true, false]) {
       }
     })
 
-    runTest(agent, server, function (segments) {
-      const [onRequestSegment, handlerSegment] = segments
+    runTest(agent, server, function (baseSegment, transaction) {
+      const [onRequestSegment, handlerSegment] = transaction.trace.getChildren(baseSegment.id)
       assertClmAttrs({
         segments: [
           {
@@ -232,7 +232,8 @@ for (const clmEnabled of [true, false]) {
       handler: { customHandler: { key1: 'val1' } }
     })
 
-    runTest(agent, server, function ([customHandlerSegment]) {
+    runTest(agent, server, function (baseSegment, transaction) {
+      const [customHandlerSegment] = transaction.trace.getChildren(baseSegment.id)
       assertClmAttrs({
         segments: [
           {
@@ -269,7 +270,8 @@ for (const clmEnabled of [true, false]) {
     }
 
     server.register(plugin).then(() => {
-      runTest(agent, server, function ([pluginHandlerSegment]) {
+      runTest(agent, server, function (baseSegment, transaction) {
+        const [pluginHandlerSegment] = transaction.trace.getChildren(baseSegment.id)
         assertClmAttrs({
           segments: [
             {
diff --git a/test/versioned/ioredis/ioredis.test.js b/test/versioned/ioredis/ioredis.test.js
index 8f4506093b..45035d7497 100644
--- a/test/versioned/ioredis/ioredis.test.js
+++ b/test/versioned/ioredis/ioredis.test.js
@@ -77,16 +77,18 @@ test('ioredis instrumentation', async (t) => {
 
     agent.on('transactionFinished', function (tx) {
       const root = tx.trace.root
-      plan.equal(root.children.length, 2, 'root has two children')
+      const children = tx.trace.getChildren(root.id)
+      plan.equal(children.length, 2, 'root has two children')
+
+      const [setSegment, getSegment] = children
 
-      const setSegment = root.children[0]
       plan.equal(setSegment.name, 'Datastore/operation/Redis/set')
 
       // ioredis operations return promise, any 'then' callbacks will be sibling segments
       // of the original redis call
-      const getSegment = root.children[1]
       plan.equal(getSegment.name, 'Datastore/operation/Redis/get')
-      plan.equal(getSegment.children.length, 0, 'should not contain any segments')
+      const getChildren = tx.trace.getChildren(getSegment.id)
+      plan.equal(getChildren.length, 0, 'should not contain any segments')
     })
 
     helper.runInTransaction(agent, async (transaction) => {
diff --git a/test/versioned/kafkajs/kafka.test.js b/test/versioned/kafkajs/kafka.test.js
index 63f1b1572a..c304bb8b7d 100644
--- a/test/versioned/kafkajs/kafka.test.js
+++ b/test/versioned/kafkajs/kafka.test.js
@@ -61,27 +61,27 @@ test('send records correctly', async (t) => {
   const expectedName = 'produce-tx'
 
   agent.on('transactionFinished', (tx) => {
-    if (tx.name !== expectedName) {
-      return
-    }
+    if (tx.name === expectedName) {
+      const name = `MessageBroker/Kafka/Topic/Produce/Named/${topic}`
+      const segment = tx.agent.tracer.getSegment()
+      const children = tx.trace.getChildren(segment.id)
 
-    const name = `MessageBroker/Kafka/Topic/Produce/Named/${topic}`
-    const segment = tx.agent.tracer.getSegment()
+      const foundSegment = children.find((s) => s.name.endsWith(topic))
+      plan.equal(foundSegment.name, name)
 
-    const foundSegment = segment.children.find((s) => s.name.endsWith(topic))
-    plan.equal(foundSegment.name, name)
+      const metric = tx.metrics.getMetric(name)
+      plan.equal(metric.callCount, 1)
+      const sendMetric = agent.metrics.getMetric(
+        'Supportability/Features/Instrumentation/kafkajs/send'
+      )
+      plan.equal(sendMetric.callCount, 1)
 
-    const metric = tx.metrics.getMetric(name)
-    plan.equal(metric.callCount, 1)
-    const sendMetric = agent.metrics.getMetric(
-      'Supportability/Features/Instrumentation/kafkajs/send'
-    )
-    plan.equal(sendMetric.callCount, 1)
+      const produceTrackingMetric = agent.metrics.getMetric(
+        `MessageBroker/Kafka/Nodes/${broker}/Produce/${topic}`
+      )
+      plan.equal(produceTrackingMetric.callCount, 1)
+    }
 
-    const produceTrackingMetric = agent.metrics.getMetric(
-      `MessageBroker/Kafka/Nodes/${broker}/Produce/${topic}`
-    )
-    plan.equal(produceTrackingMetric.callCount, 1)
   })
 
   helper.runInTransaction(agent, async (tx) => {
@@ -192,8 +192,9 @@ test('sendBatch records correctly', async (t) => {
     if (tx.name === expectedName) {
       const name = `MessageBroker/Kafka/Topic/Produce/Named/${topic}`
       const segment = tx.agent.tracer.getSegment()
+      const children = tx.trace.getChildren(segment.id)
 
-      const foundSegment = segment.children.find((s) => s.name.endsWith(topic))
+      const foundSegment = children.find((s) => s.name.endsWith(topic))
       plan.equal(foundSegment.name, name)
 
       const metric = tx.metrics.getMetric(name)
@@ -306,6 +307,7 @@ test('consume inside of a transaction', async (t) => {
       txCount++
       if (tx.name === expectedName) {
         assertSegments(
+          tx.trace,
           tx.trace.root,
           [`${SEGMENT_PREFIX}subscribe`, `${SEGMENT_PREFIX}run`],
           {
@@ -362,6 +364,7 @@ test('consume batch inside of a transaction', async (t) => {
   const txPromise = new Promise((resolve) => {
     agent.on('transactionFinished', (tx) => {
       assertSegments(
+        tx.trace,
         tx.trace.root,
         [`${SEGMENT_PREFIX}subscribe`, `${SEGMENT_PREFIX}run`],
         { exact: false },
diff --git a/test/versioned/kafkajs/utils.js b/test/versioned/kafkajs/utils.js
index 92a9306221..5a0f719870 100644
--- a/test/versioned/kafkajs/utils.js
+++ b/test/versioned/kafkajs/utils.js
@@ -95,7 +95,7 @@ utils.verifyConsumeTransaction = ({ plan, tx, topic, clientId }) => {
   )
 
   plan.equal(tx.getFullName(), expectedName)
-  const consume = metrics.findSegment(tx.trace.root, expectedName)
+  const consume = metrics.findSegment(tx.trace, tx.trace.root, expectedName)
   plan.equal(consume, tx.baseSegment)
 
   const attributes = tx.trace.attributes.get(DESTINATIONS.TRANS_SCOPE)
@@ -112,7 +112,7 @@ utils.verifyConsumeTransaction = ({ plan, tx, topic, clientId }) => {
  */
 utils.verifyDistributedTrace = ({ plan, consumeTxs, produceTx }) => {
   plan.ok(produceTx.isDistributedTrace, 'should mark producer as distributed')
-  const produceSegment = produceTx.trace.root.children[3]
+  const [, , , produceSegment] = produceTx.trace.getChildren(produceTx.trace.root.id)
   consumeTxs.forEach((consumeTx) => {
     plan.ok(consumeTx.isDistributedTrace, 'should mark consumer as distributed')
     plan.equal(consumeTx.incomingCatId, null, 'should not set old CAT properties')
diff --git a/test/versioned/koa/code-level-metrics.test.js b/test/versioned/koa/code-level-metrics.test.js
index 5975dfbb32..af7228c7fc 100644
--- a/test/versioned/koa/code-level-metrics.test.js
+++ b/test/versioned/koa/code-level-metrics.test.js
@@ -93,18 +93,21 @@ test('vanilla koa, no router', async (t) => {
         ctx.body = 'done'
       })
 
-      agent.on('transactionFinished', (tx) => {
-        const baseSegment = tx.trace.root.children[0]
+      agent.on('transactionFinished', (transaction) => {
+        const [baseSegment] = transaction.trace.getChildren(transaction.trace.root.id)
+        const [one] = transaction.trace.getChildren(baseSegment.id)
+        const [two] = transaction.trace.getChildren(one.id)
+
         assertClmAttrs(
           {
             segments: [
               {
-                segment: baseSegment.children[0],
+                segment: one, 
                 name: 'one',
                 filepath: 'code-level-metrics.test.js'
               },
               {
-                segment: baseSegment.children[0].children[0],
+                segment: two, 
                 name: 'two',
                 filepath: 'code-level-metrics.test.js'
               }
@@ -155,23 +158,27 @@ test('using koa-router', async (t) => {
       router.use('/:first', nestedRouter.routes())
       app.use(router.routes())
 
-      agent.on('transactionFinished', (tx) => {
-        const baseSegment = tx.trace.root.children[0]
+      agent.on('transactionFinished', (transaction) => {
+        const [baseSegment] = transaction.trace.getChildren(transaction.trace.root.id)
+        const [dispatch] = transaction.trace.getChildren(baseSegment.id)
+        const [appLevel] = transaction.trace.getChildren(dispatch.id)
+        const [secondMw] = transaction.trace.getChildren(appLevel.id)
+
         assertClmAttrs(
           {
             segments: [
               {
-                segment: baseSegment.children[0],
+                segment: dispatch, 
                 name: 'dispatch',
                 filepath: 'koa-router/lib/router.js'
               },
               {
-                segment: baseSegment.children[0].children[0],
+                segment: appLevel, 
                 name: 'appLevelMiddleware',
                 filepath: 'code-level-metrics.test.js'
               },
               {
-                segment: baseSegment.children[0].children[0].children[0],
+                segment: secondMw, 
                 name: 'secondMiddleware',
                 filepath: 'code-level-metrics.test.js'
               }
@@ -222,23 +229,27 @@ test('using @koa/router', async (t) => {
       router.use('/:first', nestedRouter.routes())
       app.use(router.routes())
 
-      agent.on('transactionFinished', (tx) => {
-        const baseSegment = tx.trace.root.children[0]
+      agent.on('transactionFinished', (transaction) => {
+        const [baseSegment] = transaction.trace.getChildren(transaction.trace.root.id)
+        const [dispatch] = transaction.trace.getChildren(baseSegment.id)
+        const [appLevel] = transaction.trace.getChildren(dispatch.id)
+        const [secondMw] = transaction.trace.getChildren(appLevel.id)
+
         assertClmAttrs(
           {
             segments: [
               {
-                segment: baseSegment.children[0],
+                segment: dispatch, 
                 name: 'dispatch',
                 filepath: '@koa/router/lib/router.js'
               },
               {
-                segment: baseSegment.children[0].children[0],
+                segment: appLevel, 
                 name: 'appLevelMiddleware',
                 filepath: 'code-level-metrics.test.js'
               },
               {
-                segment: baseSegment.children[0].children[0].children[0],
+                segment: secondMw, 
                 name: 'secondMiddleware',
                 filepath: 'code-level-metrics.test.js'
               }
diff --git a/test/versioned/koa/koa-route.test.js b/test/versioned/koa/koa-route.test.js
index 05f9fe5340..d6debfb8f7 100644
--- a/test/versioned/koa/koa-route.test.js
+++ b/test/versioned/koa/koa-route.test.js
@@ -35,7 +35,7 @@ test('should name and produce segments for koa-route middleware', (t, end) => {
   })
   app.use(first)
   agent.on('transactionFinished', function (tx) {
-    assertSegments(tx.trace.root, [
+    assertSegments(tx.trace, tx.trace.root, [
       'WebTransaction/WebFrameworkUri/Koa/GET//resource',
       ['Nodejs/Middleware/Koa/firstMiddleware//resource']
     ])
@@ -61,7 +61,7 @@ test('should name the transaction after the last responder', (t, end) => {
   app.use(first)
   app.use(second)
   agent.on('transactionFinished', function (tx) {
-    assertSegments(tx.trace.root, [
+    assertSegments(tx.trace, tx.trace.root, [
       'WebTransaction/WebFrameworkUri/Koa/GET//:second',
       [
         'Nodejs/Middleware/Koa/firstMiddleware//:first',
@@ -91,7 +91,7 @@ test('should name the transaction properly when responding after next', (t, end)
   app.use(first)
   app.use(second)
   agent.on('transactionFinished', function (tx) {
-    assertSegments(tx.trace.root, [
+    assertSegments(tx.trace, tx.trace.root, [
       'WebTransaction/WebFrameworkUri/Koa/GET//:first',
       [
         'Nodejs/Middleware/Koa/firstMiddleware//:first',
@@ -120,7 +120,7 @@ test('should work with early responding', (t, end) => {
   app.use(first)
   app.use(second)
   agent.on('transactionFinished', function (tx) {
-    assertSegments(tx.trace.root, [
+    assertSegments(tx.trace, tx.trace.root, [
       'WebTransaction/WebFrameworkUri/Koa/GET//:first',
       ['Nodejs/Middleware/Koa/firstMiddleware//:first']
     ])
@@ -146,7 +146,7 @@ test('should name the transaction after the source of the error that occurred',
   app.use(first)
   app.use(second)
   agent.on('transactionFinished', function (tx) {
-    assertSegments(tx.trace.root, [
+    assertSegments(tx.trace, tx.trace.root, [
       'WebTransaction/WebFrameworkUri/Koa/GET//:second',
       [
         'Nodejs/Middleware/Koa/firstMiddleware//:first',
@@ -179,7 +179,7 @@ test('should work properly when used along with non-route middleware', (t, end)
   app.use(second)
   app.use(third)
   agent.on('transactionFinished', function (tx) {
-    assertSegments(tx.trace.root, [
+    assertSegments(tx.trace, tx.trace.root, [
       'WebTransaction/WebFrameworkUri/Koa/GET//resource',
       [
         'Nodejs/Middleware/Koa/firstMiddleware',
diff --git a/test/versioned/koa/koa.test.js b/test/versioned/koa/koa.test.js
index 1ba61abb47..2c4e534128 100644
--- a/test/versioned/koa/koa.test.js
+++ b/test/versioned/koa/koa.test.js
@@ -54,6 +54,7 @@ function run({ t, expected = 'done', cb, end, plan }) {
 
 function checkSegments(plan, tx) {
   assertSegments(
+    tx.trace,
     tx.trace.root,
     [
       // Until koa-router is instrumented and transaction naming is addressed,
@@ -292,6 +293,7 @@ test('correctly records actions interspersed among middleware', async (t) => {
 
   agent.on('transactionFinished', (tx) => {
     assertSegments(
+      tx.trace,
       tx.trace.root,
       [
         'WebTransaction/WebFrameworkUri/Koa/GET//',
@@ -355,6 +357,7 @@ test('maintains transaction state between middleware', async (t) => {
 
   agent.on('transactionFinished', function (txn) {
     assertSegments(
+      tx.trace,
       tx.trace.root,
       [
         txn.name,
diff --git a/test/versioned/koa/router-common.js b/test/versioned/koa/router-common.js
index 7e84aff9d0..d4c7757054 100644
--- a/test/versioned/koa/router-common.js
+++ b/test/versioned/koa/router-common.js
@@ -96,7 +96,7 @@ module.exports = (pkg) => {
 
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:first',
             [
               'Koa/Router: /',
@@ -123,7 +123,7 @@ module.exports = (pkg) => {
         })
         app.use(router.middleware())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:first',
             ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//:first']]
           ])
@@ -158,7 +158,7 @@ module.exports = (pkg) => {
         })
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//.*rst$',
             ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//.*rst$/']]
           ])
@@ -183,7 +183,7 @@ module.exports = (pkg) => {
         })
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             `WebTransaction/WebFrameworkUri/Koa/GET//:first/${path}`,
             ['Koa/Router: /', [`Nodejs/Middleware/Koa/firstMiddleware//:first/${path}`]]
           ])
@@ -208,7 +208,7 @@ module.exports = (pkg) => {
         })
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:first',
             [
               'Koa/Router: /',
@@ -243,7 +243,7 @@ module.exports = (pkg) => {
           app.silent = true
           app.use(router.routes())
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET//:first',
               [
                 'Koa/Router: /',
@@ -280,7 +280,7 @@ module.exports = (pkg) => {
 
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:second',
             [
               'Koa/Router: /',
@@ -314,7 +314,7 @@ module.exports = (pkg) => {
 
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:first',
             [
               'Nodejs/Middleware/Koa/errorHandler',
@@ -345,7 +345,7 @@ module.exports = (pkg) => {
 
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:first',
             [
               'Nodejs/Middleware/Koa/errorHandler',
@@ -390,7 +390,7 @@ module.exports = (pkg) => {
               ]
           app.use(router.routes())
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET//:second',
               ['Koa/Router: /', segmentTree]
             ])
@@ -415,7 +415,7 @@ module.exports = (pkg) => {
         })
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET/(not found)',
             ['Koa/Router: /']
           ])
@@ -444,7 +444,7 @@ module.exports = (pkg) => {
           })
           app.use(router.routes())
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET/(not found)',
               ['Nodejs/Middleware/Koa/baseMiddleware', ['Koa/Router: /']]
             ])
@@ -484,7 +484,7 @@ module.exports = (pkg) => {
           // the dispatch function blocking its returned promise on the
           // resolution of a recursively returned promise.
           // https://github.com/koajs/compose/blob/e754ca3c13e9248b3f453d98ea0b618e09578e2d/index.js#L42-L44
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:second',
             [
               'Koa/Router: /',
@@ -522,7 +522,7 @@ module.exports = (pkg) => {
           // the dispatch function blocking its returned promise on the
           // resolution of a recursively returned promise.
           // https://github.com/koajs/compose/blob/e754ca3c13e9248b3f453d98ea0b618e09578e2d/index.js#L42-L44
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:second',
             [
               'Koa/Router: /',
@@ -556,7 +556,7 @@ module.exports = (pkg) => {
         router.use('/:first', router2.routes())
         app.use(router.routes())
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:first/:second',
             ['Koa/Router: /', [getNestedSpanName('secondMiddleware')]]
           ])
@@ -586,7 +586,7 @@ module.exports = (pkg) => {
         app.use(router.routes())
 
         agent.on('transactionFinished', (tx) => {
-          assertSegments(tx.trace.root, [
+          assertSegments(tx.trace, tx.trace.root, [
             'WebTransaction/WebFrameworkUri/Koa/GET//:first/:second',
             [
               'Nodejs/Middleware/Koa/appLevelMiddleware',
@@ -620,7 +620,7 @@ module.exports = (pkg) => {
           app.use(router.routes())
 
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET//:first/:second',
               [
                 'Nodejs/Middleware/Koa/appLevelMiddleware',
@@ -658,7 +658,7 @@ module.exports = (pkg) => {
             app.use(router.routes())
             app.use(router.allowedMethods({ throw: true }))
             agent.on('transactionFinished', (tx) => {
-              assertSegments(tx.trace.root, [
+              assertSegments(tx.trace, tx.trace.root, [
                 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)',
                 ['Koa/Router: /', [`Nodejs/Middleware/Koa/${allowedMethodsFnName}`]]
               ])
@@ -683,7 +683,7 @@ module.exports = (pkg) => {
           app.use(router.routes())
           app.use(router.allowedMethods({ throw: true }))
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)',
               ['Koa/Router: /', [`Nodejs/Middleware/Koa/${allowedMethodsFnName}`]]
             ])
@@ -721,7 +721,7 @@ module.exports = (pkg) => {
             app.use(router.allowedMethods({ throw: true }))
 
             agent.on('transactionFinished', (tx) => {
-              assertSegments(tx.trace.root, [
+              assertSegments(tx.trace, tx.trace.root, [
                 'WebTransaction/NormalizedUri/*',
                 [
                   'Nodejs/Middleware/Koa/errorHandler',
@@ -761,7 +761,7 @@ module.exports = (pkg) => {
             app.use(router.allowedMethods({ throw: true }))
 
             agent.on('transactionFinished', (tx) => {
-              assertSegments(tx.trace.root, [
+              assertSegments(tx.trace, tx.trace.root, [
                 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)',
                 [
                   'Nodejs/Middleware/Koa/baseMiddleware',
@@ -795,7 +795,7 @@ module.exports = (pkg) => {
             app.use(router.routes())
             app.use(router.allowedMethods())
             agent.on('transactionFinished', (tx) => {
-              assertSegments(tx.trace.root, [
+              assertSegments(tx.trace, tx.trace.root, [
                 'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)',
                 ['Koa/Router: /', [`Nodejs/Middleware/Koa/${allowedMethodsFnName}`]]
               ])
@@ -820,7 +820,7 @@ module.exports = (pkg) => {
           app.use(router.routes())
           app.use(router.allowedMethods())
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)',
               ['Koa/Router: /', [`Nodejs/Middleware/Koa/${allowedMethodsFnName}`]]
             ])
@@ -852,7 +852,7 @@ module.exports = (pkg) => {
           app.use(router.allowedMethods())
 
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET/(method not allowed)',
               [
                 'Nodejs/Middleware/Koa/appLevelMiddleware',
@@ -886,7 +886,7 @@ module.exports = (pkg) => {
           app.use(router.allowedMethods())
 
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET/(not implemented)',
               [
                 'Nodejs/Middleware/Koa/appLevelMiddleware',
@@ -912,7 +912,7 @@ module.exports = (pkg) => {
           app.use(router.routes())
           app.use(router.allowedMethods())
           agent.on('transactionFinished', (tx) => {
-            assertSegments(tx.trace.root, [
+            assertSegments(tx.trace, tx.trace.root, [
               'WebTransaction/WebFrameworkUri/Koa/GET//:first',
               ['Koa/Router: /', ['Nodejs/Middleware/Koa/firstMiddleware//:first']]
             ])
diff --git a/test/versioned/langchain/common.js b/test/versioned/langchain/common.js
index ebd48728cd..cb91dd7d2d 100644
--- a/test/versioned/langchain/common.js
+++ b/test/versioned/langchain/common.js
@@ -25,10 +25,11 @@ function assertLangChainVectorSearch(
   { tx, vectorSearch, responseDocumentSize },
   { assert = require('node:assert') } = {}
 ) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const expectedSearch = {
     'id': /[a-f0-9]{36}/,
     'appName': 'New Relic for Node.js tests',
-    'span_id': tx.trace.root.children[0].id,
+    'span_id': segment.id,
     'trace_id': tx.traceId,
     'request.k': 1,
     'request.query': 'This is an embedding test.',
@@ -36,7 +37,7 @@ function assertLangChainVectorSearch(
     'vendor': 'langchain',
     'virtual_llm': true,
     ['response.number_of_documents']: responseDocumentSize,
-    'duration': tx.trace.root.children[0].getDurationInMillis()
+    'duration': segment.getDurationInMillis()
   }
 
   assert.equal(vectorSearch[0].type, 'LlmVectorSearch')
@@ -47,11 +48,12 @@ function assertLangChainVectorSearchResult(
   { tx, vectorSearchResult, vectorSearchId },
   { assert = require('node:assert') } = {}
 ) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const baseSearchResult = {
     'id': /[a-f0-9]{36}/,
     'search_id': vectorSearchId,
     'appName': 'New Relic for Node.js tests',
-    'span_id': tx.trace.root.children[0].id,
+    'span_id': segment.id,
     'trace_id': tx.traceId,
     'ingest_source': 'Node',
     'vendor': 'langchain',
@@ -78,10 +80,11 @@ function assertLangChainChatCompletionSummary(
   { tx, chatSummary, withCallback },
   { assert = require('node:assert') } = {}
 ) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const expectedSummary = {
     'id': /[a-f0-9]{36}/,
     'appName': 'New Relic for Node.js tests',
-    'span_id': tx.trace.root.children[0].id,
+    'span_id': segment.id,
     'trace_id': tx.traceId,
     'request_id': undefined,
     'ingest_source': 'Node',
@@ -91,7 +94,7 @@ function assertLangChainChatCompletionSummary(
     'tags': 'tag1,tag2',
     'virtual_llm': true,
     ['response.number_of_messages']: 1,
-    'duration': tx.trace.root.children[0].getDurationInMillis()
+    'duration': segment.getDurationInMillis()
   }
 
   if (withCallback) {
@@ -114,10 +117,11 @@ function assertLangChainChatCompletionMessages(
   },
   { assert = require('node:assert') } = {}
 ) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const baseMsg = {
     id: /[a-f0-9]{36}/,
     appName: 'New Relic for Node.js tests',
-    span_id: tx.trace.root.children[0].id,
+    span_id: segment.id,
     trace_id: tx.traceId,
     ingest_source: 'Node',
     vendor: 'langchain',
diff --git a/test/versioned/langchain/runnables-streaming.test.js b/test/versioned/langchain/runnables-streaming.test.js
index 1c81f61ddf..084e296e3c 100644
--- a/test/versioned/langchain/runnables-streaming.test.js
+++ b/test/versioned/langchain/runnables-streaming.test.js
@@ -404,7 +404,7 @@ test('streaming enabled', async (t) => {
         // no-op
       }
 
-      assertSegments(tx.trace.root, ['Llm/chain/Langchain/stream'], { exact: false })
+      assertSegments(tx.trace, tx.trace.root, ['Llm/chain/Langchain/stream'], { exact: false })
 
       tx.end()
       end()
diff --git a/test/versioned/langchain/runnables.test.js b/test/versioned/langchain/runnables.test.js
index 8a2037624b..39c79d33d6 100644
--- a/test/versioned/langchain/runnables.test.js
+++ b/test/versioned/langchain/runnables.test.js
@@ -358,7 +358,7 @@ test('should create span on successful runnables create', (t, end) => {
     const result = await chain.invoke(input, options)
 
     assert.ok(result)
-    assertSegments(tx.trace.root, ['Llm/chain/Langchain/invoke'], { exact: false })
+    assertSegments(tx.trace, tx.trace.root, ['Llm/chain/Langchain/invoke'], { exact: false })
 
     tx.end()
     end()
diff --git a/test/versioned/langchain/tools.test.js b/test/versioned/langchain/tools.test.js
index 9b4654c7de..52db9d8b78 100644
--- a/test/versioned/langchain/tools.test.js
+++ b/test/versioned/langchain/tools.test.js
@@ -44,7 +44,7 @@ test('should create span on successful tools create', (t, end) => {
   helper.runInTransaction(agent, async (tx) => {
     const result = await tool.call(input)
     assert.ok(result)
-    assertSegments(tx.trace.root, ['Llm/tool/Langchain/node-agent-test-tool'], { exact: false })
+    assertSegments(tx.trace, tx.trace.root, ['Llm/tool/Langchain/node-agent-test-tool'], { exact: false })
     tx.end()
     end()
   })
@@ -75,10 +75,11 @@ test('should create LlmTool event for every tool.call', (t, end) => {
     assert.equal(events.length, 1, 'should create a LlmTool event')
     const [[{ type }, toolEvent]] = events
     assert.equal(type, 'LlmTool')
+    const [segment] = tx.trace.getChildren(tx.trace.root.id)
     match(toolEvent, {
       'id': /[a-f0-9]{36}/,
       'appName': 'New Relic for Node.js tests',
-      'span_id': tx.trace.root.children[0].id,
+      'span_id': segment.id,
       'trace_id': tx.traceId,
       'ingest_source': 'Node',
       'vendor': 'langchain',
@@ -89,7 +90,7 @@ test('should create LlmTool event for every tool.call', (t, end) => {
       'output': tool.fakeData[input],
       'name': tool.name,
       'description': tool.description,
-      'duration': tx.trace.root.children[0].getDurationInMillis(),
+      'duration': segment.getDurationInMillis(),
       'run_id': undefined
     })
     tx.end()
diff --git a/test/versioned/langchain/vectorstore.test.js b/test/versioned/langchain/vectorstore.test.js
index 57d3c71799..39401fb115 100644
--- a/test/versioned/langchain/vectorstore.test.js
+++ b/test/versioned/langchain/vectorstore.test.js
@@ -95,7 +95,7 @@ test('should create span on successful vectorstore create', (t, end) => {
   helper.runInTransaction(agent, async (tx) => {
     const result = await vs.similaritySearch('This is an embedding test.', 1)
     assert.ok(result)
-    assertSegments(tx.trace.root, ['Llm/vectorstore/Langchain/similaritySearch'], {
+    assertSegments(tx.trace, tx.trace.root, ['Llm/vectorstore/Langchain/similaritySearch'], {
       exact: false
     })
     tx.end()
diff --git a/test/versioned/memcached/memcached.test.js b/test/versioned/memcached/memcached.test.js
index f9762b9a1c..0d8ff7b6ec 100644
--- a/test/versioned/memcached/memcached.test.js
+++ b/test/versioned/memcached/memcached.test.js
@@ -56,6 +56,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/touch'],
             { exact: false },
@@ -91,6 +92,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/get', ['Truncated/Callback: <anonymous>']],
             { exact: false },
@@ -126,6 +128,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/gets', ['Truncated/Callback: <anonymous>']],
             { exact: false },
@@ -161,6 +164,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/get', ['Truncated/Callback: handle']],
             { exact: false },
@@ -196,6 +200,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/set', ['Truncated/Callback: <anonymous>']],
             { exact: false },
@@ -234,6 +239,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
             transaction.end()
             assertSegments(
+              transaction.trace,
               transaction.trace.root,
               ['Datastore/operation/Memcache/replace', ['Truncated/Callback: <anonymous>']],
               { exact: false },
@@ -270,6 +276,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/add', ['Truncated/Callback: <anonymous>']],
             { exact: false },
@@ -311,6 +318,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
               transaction.end()
               assertSegments(
+                transaction.trace,
                 transaction.trace.root,
                 ['Datastore/operation/Memcache/cas', ['Truncated/Callback: <anonymous>']],
                 { exact: false },
@@ -349,6 +357,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
             plan.ok(agent.getTransaction(), 'transaction should still be visible')
             transaction.end()
             assertSegments(
+              transaction.trace,
               transaction.trace.root,
               ['Datastore/operation/Memcache/append', ['Truncated/Callback: <anonymous>']],
               { exact: false },
@@ -386,6 +395,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
             plan.ok(agent.getTransaction(), 'transaction should still be visible')
             transaction.end()
             assertSegments(
+              transaction.trace,
               transaction.trace.root,
               ['Datastore/operation/Memcache/prepend', ['Truncated/Callback: <anonymous>']],
               { exact: false },
@@ -423,6 +433,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
             plan.ok(agent.getTransaction(), 'transaction should still be visible')
             transaction.end()
             assertSegments(
+              transaction.trace,
               transaction.trace.root,
               ['Datastore/operation/Memcache/delete', ['Truncated/Callback: <anonymous>']],
               { exact: false },
@@ -459,6 +470,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/incr', ['Truncated/Callback: <anonymous>']],
             { exact: false },
@@ -494,6 +506,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/decr'],
             { exact: false },
@@ -532,6 +545,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
 
           transaction.end()
           assertSegments(
+            transaction.trace,
             transaction.trace.root,
             ['Datastore/operation/Memcache/version'],
             { exact: false },
@@ -587,7 +601,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err, 'should not throw an error')
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           plan.equal(segment.getAttributes().key, '"foo"', 'should have the get key as a parameter')
         })
       })
@@ -604,7 +618,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err)
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           plan.ok(!segment.getAttributes().key, 'should not have any attributes')
         })
       })
@@ -620,7 +634,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err, 'should not throw an error')
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           plan.equal(
             segment.getAttributes().key,
             '["foo","bar"]',
@@ -640,7 +654,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err, 'should not throw an error')
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           plan.equal(segment.getAttributes().key, '"foo"', 'should have the set key as a parameter')
         })
       })
@@ -677,7 +691,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err, 'should not throw an error')
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           const attributes = segment.getAttributes()
           plan.equal(
             attributes.host,
@@ -706,7 +720,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err, 'should not throw an error')
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           const attributes = segment.getAttributes()
           plan.equal(
             attributes.host,
@@ -758,7 +772,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err, 'should not throw an error')
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           const attributes = segment.getAttributes()
           plan.equal(attributes.host, undefined, 'should not have host instance parameter')
           plan.equal(
@@ -786,7 +800,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
           plan.ok(!err, 'should not throw an error')
 
           transaction.end()
-          const segment = transaction.trace.root.children[0]
+          const [segment] = transaction.trace.getChildren(transaction.trace.root.id)
           const attributes = segment.getAttributes()
           plan.equal(attributes.host, undefined, 'should not have host instance parameter')
           plan.equal(
@@ -854,13 +868,13 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
       helper.runInTransaction(agent, function (transaction) {
         memcached.get('foo', function (err) {
           assert.ok(!err)
-          const firstSegment = agent.tracer.getSegment().parent
+          const firstSegment = transaction.trace.getParent(agent.tracer.getSegment().parentId)
 
           memcached.get('bar', function (err) {
             assert.ok(!err)
             transaction.end()
             checkParams(firstSegment, 'server1', '1111')
-            checkParams(agent.tracer.getSegment().parent, 'server2', '2222')
+            checkParams(transaction.trace.getParent(agent.tracer.getSegment().parentId), 'server2', '2222')
             end()
           })
         })
@@ -872,8 +886,7 @@ test('memcached instrumentation', { timeout: 5000 }, async function (t) {
       helper.runInTransaction(agent, function (transaction) {
         memcached.getMulti(['foo', 'bar'], function (err) {
           assert.ok(!err)
-          const firstGet = transaction.trace.root.children[0]
-          const secondGet = transaction.trace.root.children[1]
+          const [firstGet, secondGet] = transaction.trace.getChildren(transaction.trace.root.id)
           if (firstGet.getAttributes().host === 'server1') {
             checkParams(firstGet, 'server1', '1111')
             checkParams(secondGet, 'server2', '2222')
diff --git a/test/versioned/mongodb-esm/db.test.mjs b/test/versioned/mongodb-esm/db.test.mjs
index 805c059969..0e81485f28 100644
--- a/test/versioned/mongodb-esm/db.test.mjs
+++ b/test/versioned/mongodb-esm/db.test.mjs
@@ -461,11 +461,12 @@ function verifyMongoSegments({ t, tx, expectedSegments }) {
   let current = tx.trace.root
 
   for (let i = 0, l = expectedSegments.length; i < l; i += 1) {
+    let children = tx.trace.getChildren(current.id)
     // Filter out net.createConnection segments as they could occur during
     // execution, and we don't need to verify them.
-    current.children = current.children.filter((c) => c.name !== 'net.createConnection')
-    assert.equal(current.children.length, 1, 'should have one child segment')
-    current = current.children[0]
+    children = children.filter((c) => c.name !== 'net.createConnection')
+    assert.equal(children.length, 1, 'should have one child segment')
+    current = children[0]
     assert.equal(
       current.name,
       expectedSegments[i],
diff --git a/test/versioned/mongodb-esm/test-assertions.mjs b/test/versioned/mongodb-esm/test-assertions.mjs
index bd6fc20b82..97044bc10a 100644
--- a/test/versioned/mongodb-esm/test-assertions.mjs
+++ b/test/versioned/mongodb-esm/test-assertions.mjs
@@ -19,6 +19,7 @@ function getValidatorCallback({ t, tx, segments, metrics, end, childrenLength =
 
     const segment = agent.tracer.getSegment()
     let current = tx.trace.root
+    let children = tx.trace.getChildren(current.id)
 
     if (childrenLength === 2) {
       // This block is for testing `collection.aggregate`. The `aggregate`
@@ -29,27 +30,29 @@ function getValidatorCallback({ t, tx, segments, metrics, end, childrenLength =
       // on the trace root. We also added a strict flag for `aggregate` because,
       // depending on the version, there is an extra segment for the callback
       // of our test which we do not need to assert.
-      assert.equal(current.children.length, childrenLength, 'should have two children')
+      assert.equal(children.length, childrenLength, 'should have two children')
       for (const [i, expectedSegment] of segments.entries()) {
-        const child = current.children[i]
+        const child = children[i]
+        const childChildren = tx.trace.getChildren(child.id)
         assert.equal(child.name, expectedSegment, `child should be named ${expectedSegment}`)
         if (common.MONGO_SEGMENT_RE.test(child.name) === true) {
           checkSegmentParams(child, METRIC_HOST_NAME, METRIC_HOST_PORT)
           assert.equal(child.ignore, false, 'should not ignore segment')
         }
-        assert.equal(child.children.length, 0, 'should have no more children')
+        assert.equal(childChildren.length, 0, 'should have no more children')
       }
     } else {
       for (let i = 0, l = segments.length; i < l; ++i) {
-        assert.equal(current.children.length, 1, 'should have one child')
-        current = current.children[0]
+        assert.equal(children.length, 1, 'should have one child')
+        current = children[0]
+        children = tx.trace.getChildren(current.id)
         assert.equal(current.name, segments[i], 'child should be named ' + segments[i])
         if (common.MONGO_SEGMENT_RE.test(current.name) === true) {
           checkSegmentParams(current, METRIC_HOST_NAME, METRIC_HOST_PORT)
           assert.equal(current.ignore, false, 'should not ignore segment')
         }
       }
-      assert.equal(current.children.length, 0, 'should have no more children')
+      assert.equal(children.length, 0, 'should have no more children')
     }
     assert.equal(current === segment, true, 'should test to the current segment')
 
diff --git a/test/versioned/mongodb/collection-common.js b/test/versioned/mongodb/collection-common.js
index e3aa23bb96..5d0939f7cb 100644
--- a/test/versioned/mongodb/collection-common.js
+++ b/test/versioned/mongodb/collection-common.js
@@ -118,6 +118,7 @@ function collectionTest(name, run) {
               )
               const segment = agent.tracer.getSegment()
               let current = transaction.trace.root
+              const children = transaction.trace.getChildren(current.id)
 
               // this logic is just for the collection.aggregate.
               // aggregate no longer returns a callback with cursor
@@ -130,10 +131,11 @@ function collectionTest(name, run) {
               // there is an extra segment for the callback of our test which we do not care
               // to assert
               if (childrenLength === 2) {
-                assert.equal(current.children.length, childrenLength, 'should have one child')
+                assert.equal(children.length, childrenLength, 'should have one child')
 
                 segments.forEach((expectedSegment, i) => {
-                  const child = current.children[i]
+                  const child = children[i]
+                  const childChildren = transaction.trace.getChildren(child.id)
 
                   assert.equal(
                     child.name,
@@ -146,13 +148,15 @@ function collectionTest(name, run) {
                   }
 
                   if (strict) {
-                    assert.equal(child.children.length, 0, 'should have no more children')
+                    assert.equal(childChildren.length, 0, 'should have no more children')
                   }
                 })
               } else {
+                let currentChildren
                 for (let i = 0, l = segments.length; i < l; ++i) {
-                  assert.equal(current.children.length, childrenLength, 'should have one child')
-                  current = current.children[0]
+                  assert.equal(children.length, childrenLength, 'should have one child')
+                  current = children[0]
+                  currentChildren = transaction.trace.getChildren(current.id)
                   assert.equal(current.name, segments[i], 'child should be named ' + segments[i])
                   if (common.MONGO_SEGMENT_RE.test(current.name)) {
                     checkSegmentParams(current)
@@ -161,7 +165,7 @@ function collectionTest(name, run) {
                 }
 
                 if (strict) {
-                  assert.equal(current.children.length, 0, 'should have no more children')
+                  assert.equal(currentChildren.length, 0, 'should have no more children')
                 }
               }
 
@@ -202,7 +206,7 @@ function collectionTest(name, run) {
                 assert.ok(attributes.database_name, 'should have database name attribute')
                 assert.ok(attributes.product, 'should have product attribute')
               }
-              current = current.children[0]
+              ;[current] = tx.trace.getChildren(current.id)
             }
             end()
           })
@@ -229,7 +233,7 @@ function collectionTest(name, run) {
                 )
                 assert.ok(attributes.product, 'should have product attribute')
               }
-              current = current.children[0]
+              ;[current] = tx.trace.getChildren(current.id)
             }
             end()
           })
@@ -278,6 +282,7 @@ function collectionTest(name, run) {
               )
               const segment = agent.tracer.getSegment()
               let current = transaction.trace.root
+              const children = transaction.trace.getChildren(current.id)
 
               // this logic is just for the collection.aggregate.
               // aggregate no longer returns a callback with cursor
@@ -290,10 +295,11 @@ function collectionTest(name, run) {
               // there is an extra segment for the callback of our test which we do not care
               // to assert
               if (childrenLength === 2) {
-                assert.equal(current.children.length, childrenLength, 'should have one child')
+                assert.equal(children.length, childrenLength, 'should have one child')
 
                 segments.forEach((expectedSegment, i) => {
-                  const child = current.children[i]
+                  const child = children[i]
+                  const childChildren = transaction.trace.getChildren(child.id)
 
                   assert.equal(
                     child.name,
@@ -306,13 +312,15 @@ function collectionTest(name, run) {
                   }
 
                   if (strict) {
-                    assert.equal(child.children.length, 0, 'should have no more children')
+                    assert.equal(childChildren.length, 0, 'should have no more children')
                   }
                 })
               } else {
+                let currentChildren
                 for (let i = 0, l = segments.length; i < l; ++i) {
-                  assert.equal(current.children.length, childrenLength, 'should have one child')
-                  current = current.children[0]
+                  assert.equal(children.length, childrenLength, 'should have one child')
+                  current = children[0]
+                  currentChildren = transaction.trace.getChildren(current.id)
                   assert.equal(current.name, segments[i], 'child should be named ' + segments[i])
                   if (common.MONGO_SEGMENT_RE.test(current.name)) {
                     checkSegmentParams(current)
@@ -321,7 +329,7 @@ function collectionTest(name, run) {
                 }
 
                 if (strict) {
-                  assert.equal(current.children.length, 0, 'should have no more children')
+                  assert.equal(currentChildren.length, 0, 'should have no more children')
                 }
               }
 
diff --git a/test/versioned/mongodb/db-common.js b/test/versioned/mongodb/db-common.js
index 4e0edac58b..b102a884e1 100644
--- a/test/versioned/mongodb/db-common.js
+++ b/test/versioned/mongodb/db-common.js
@@ -79,15 +79,16 @@ function verifyMongoSegments(agent, transaction, names, opts) {
   let child
 
   for (let i = 0, l = names.length; i < l; ++i) {
+    let children = transaction.trace.getChildren(current.id)
     if (opts.legacy) {
       // Filter out net.createConnection segments as they could occur during execution, which is fine
       // but breaks out assertion function
-      current.children = current.children.filter((c) => c.name !== 'net.createConnection')
-      assert.equal(current.children.length, 1, 'should have one child segment')
-      child = current.children[0]
-      current = current.children[0]
+      children = children.filter((c) => c.name !== 'net.createConnection')
+      assert.equal(children.length, 1, 'should have one child segment')
+      child = children[0]
+      current = children[0]
     } else {
-      child = current.children[i]
+      child = children[i]
     }
     assert.equal(child.name, names[i], 'segment should be named ' + names[i])
 
diff --git a/test/versioned/mysql/basic-pool.js b/test/versioned/mysql/basic-pool.js
index 17a7b3bff5..1b3cedc458 100644
--- a/test/versioned/mysql/basic-pool.js
+++ b/test/versioned/mysql/basic-pool.js
@@ -121,9 +121,12 @@ module.exports = function ({ factory, constants, pkgVersion }) {
       const { agent, pool } = t.nr
       helper.runInTransaction(agent, function transactionInScope(txn) {
         pool.query('SELECT 1 + 1 AS solution', function (err) {
-          const seg = txn.trace.root.children[0].children.filter(function (trace) {
+          const [firstChild] = txn.trace.getChildren(txn.trace.root.id)
+          const children = txn.trace.getChildren(firstChild.id)
+
+          const [seg] = children.filter(function (trace) {
             return /Datastore\/statement\/MySQL/.test(trace.name)
-          })[0]
+          })
 
           const attributes = seg.getAttributes()
           assert.ok(!err, 'should not error')
@@ -147,7 +150,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
       helper.runInTransaction(agent, function transactionInScope(txn) {
         agent.config.datastore_tracer.instance_reporting.enabled = false
         pool.query('SELECT 1 + 1 AS solution', function (err) {
-          const seg = getDatastoreSegment(agent.tracer.getSegment())
+          const seg = getDatastoreSegment({ trace: txn.trace, segment: agent.tracer.getSegment() })
           assert.ok(!err, 'should not error making query')
           assert.ok(seg, 'should have a segment')
 
@@ -168,7 +171,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
       helper.runInTransaction(agent, function transactionInScope(txn) {
         agent.config.datastore_tracer.database_name_reporting.enabled = false
         pool.query('SELECT 1 + 1 AS solution', function (err) {
-          const seg = getDatastoreSegment(agent.tracer.getSegment())
+          const seg = getDatastoreSegment({ trace: txn.trace, segment: agent.tracer.getSegment() })
           const attributes = seg.getAttributes()
           assert.ok(!err, 'no errors')
           assert.ok(seg, 'there is a segment')
@@ -200,7 +203,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
           // In the case where you don't have a server running on
           // localhost the data will still be correctly associated
           // with the query.
-          const seg = getDatastoreSegment(agent.tracer.getSegment())
+          const seg = getDatastoreSegment({ trace: txn.trace, segment: agent.tracer.getSegment() })
           const attributes = seg.getAttributes()
           assert.ok(seg, 'there is a segment')
           assert.equal(attributes.host, agent.config.getHostnameSafe(), 'set host')
@@ -221,7 +224,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
       const defaultPool = mysql.createPool(defaultConfig)
       helper.runInTransaction(agent, function transactionInScope(txn) {
         defaultPool.query('SELECT 1 + 1 AS solution', function (err) {
-          const seg = getDatastoreSegment(agent.tracer.getSegment())
+          const seg = getDatastoreSegment({ trace: txn.trace, segment: agent.tracer.getSegment() })
           const attributes = seg.getAttributes()
 
           assert.ok(!err, 'should not error making query')
@@ -268,7 +271,8 @@ module.exports = function ({ factory, constants, pkgVersion }) {
       helper.runInTransaction(agent, function transactionInScope(txn) {
         pool.query('SELECT 1 + 1 AS solution123123123123', function (err) {
           const transaction = agent.getTransaction()
-          const segment = agent.tracer.getSegment().parent
+          const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
+
 
           assert.ok(!err, 'no error occurred')
           assert.ok(transaction, 'transaction should exist')
@@ -290,7 +294,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
           assert.ok(!err)
           assert.ok(transaction, 'should not lose transaction')
           if (transaction) {
-            const segment = agent.tracer.getSegment().parent
+            const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
             assert.ok(segment, 'segment should exist')
             assert.ok(segment.timer.start > 0, 'starts at a positive time')
             assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -315,7 +319,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
 
           connection.query('SELECT 1 + 1 AS solution', function (err) {
             const transaction = agent.getTransaction()
-            const segment = agent.tracer.getSegment().parent
+            const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
 
             assert.ok(!err, 'no error occurred')
             assert.ok(transaction, 'transaction should exist')
@@ -345,7 +349,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
             assert.ok(!err)
             assert.ok(transaction, 'should not lose transaction')
             if (transaction) {
-              const segment = agent.tracer.getSegment().parent
+              const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
               assert.ok(segment, 'segment should exist')
               assert.ok(segment.timer.start > 0, 'starts at a positive time')
               assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -373,7 +377,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
           socketPool.query('SELECT 1 + 1 AS solution', function (err) {
             assert.ok(!err, 'should not error making query')
 
-            const seg = getDatastoreSegment(agent.tracer.getSegment())
+            const seg = getDatastoreSegment({ trace: txn.trace, segment: agent.tracer.getSegment() })
             const attributes = seg.getAttributes()
 
             // In the case where you don't have a server running on localhost
@@ -456,7 +460,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
             const transaction = agent.getTransaction()
             assert.ok(transaction, 'transaction should exist')
             assert.equal(transaction.id, txn.id, 'transaction must be same')
-            const segment = agent.tracer.getSegment().parent
+            const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
             assert.ok(segment, 'segment should exist')
             assert.ok(segment.timer.start > 0, 'starts at a positive time')
             assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -494,7 +498,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
             const transaction = agent.getTransaction()
             assert.ok(transaction, 'transaction should exist')
             assert.equal(transaction.id, txn.id, 'transaction must be same')
-            const segment = agent.tracer.getSegment().parent
+            const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
             assert.ok(segment, 'segment should exist')
             assert.ok(segment.timer.start > 0, 'starts at a positive time')
             assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -532,7 +536,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
             const transaction = agent.getTransaction()
             assert.ok(transaction, 'transaction should exist')
             assert.equal(transaction.id, txn.id, 'transaction must be same')
-            const segment = agent.tracer.getSegment().parent
+            const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
             assert.ok(segment, 'segment should exist')
             assert.ok(segment.timer.start > 0, 'starts at a positive time')
             assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -569,7 +573,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
             const transaction = agent.getTransaction()
             assert.ok(transaction, 'transaction should exist')
             assert.equal(transaction.id, txn.id, 'transaction must be same')
-            const segment = agent.tracer.getSegment().parent
+            const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
             assert.ok(segment, 'segment should exist')
             assert.ok(segment.timer.start > 0, 'starts at a positive time')
             assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -608,7 +612,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
             const currentTransaction = agent.getTransaction()
             assert.ok(currentTransaction, 'transaction should exist')
             assert.equal(currentTransaction.id, txn.id, 'transaction must be same')
-            const segment = agent.tracer.getSegment().parent
+            const segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
             assert.ok(segment, 'segment should exist')
             assert.ok(segment.timer.start > 0, 'starts at a positive time')
             assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -635,7 +639,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
             assert.ok(transaction, 'transaction should exist')
             assert.equal(transaction, txn, 'transaction must be same')
 
-            let segment = agent.tracer.getSegment().parent
+            let segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
             assert.ok(segment, 'segment should exist')
             assert.ok(segment.timer.start > 0, 'starts at a positive time')
             assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -655,7 +659,7 @@ module.exports = function ({ factory, constants, pkgVersion }) {
               assert.ok(transaction, 'transaction should exist')
               assert.equal(transaction, txn, 'transaction must be same')
 
-              segment = agent.tracer.getSegment().parent
+              segment = txn.trace.getParent(agent.tracer.getSegment().parentId)
               assert.ok(segment, 'segment should exist')
               assert.ok(segment.timer.start > 0, 'starts at a positive time')
               assert.ok(segment.timer.start <= Date.now(), 'starts in past')
@@ -695,8 +699,8 @@ async function getDomainSocketPath() {
   }
 }
 
-function getDatastoreSegment(segment) {
-  return segment.parent.children.filter(function (s) {
+function getDatastoreSegment({ segment, trace }) {
+  return trace.getChildren(trace.getParent(segment.parentId).id).filter(function (s) {
     return /^Datastore/.test(s && s.name)
   })[0]
 }
diff --git a/test/versioned/mysql/basic.js b/test/versioned/mysql/basic.js
index 8172194d2f..e53a72c2aa 100644
--- a/test/versioned/mysql/basic.js
+++ b/test/versioned/mysql/basic.js
@@ -15,6 +15,7 @@ const urltils = require('../../../lib/util/urltils')
 const params = require('../../lib/params')
 const setup = require('./setup')
 const { getClient } = require('./utils')
+const { findSegment } = require('../../lib/metrics_helper')
 
 module.exports = function ({ lib, factory, poolFactory, constants }) {
   const { USER, DATABASE, TABLE } = constants
@@ -144,7 +145,7 @@ module.exports = function ({ lib, factory, poolFactory, constants }) {
     await t.test('ensure database name changes with a use statement', function (t, end) {
       const { agent, pool } = t.nr
       assert.ok(!agent.getTransaction(), 'no transaction should be in play yet')
-      helper.runInTransaction(agent, function transactionInScope() {
+      helper.runInTransaction(agent, function transactionInScope(tx) {
         assert.ok(agent.getTransaction(), 'we should be in a transaction')
         getClient(pool, function (err, client) {
           assert.ok(!err)
@@ -155,7 +156,7 @@ module.exports = function ({ lib, factory, poolFactory, constants }) {
               assert.ok(!err, 'should not fail to set database')
 
               client.query('SELECT 1 + 1 AS solution', function (err) {
-                const seg = agent.tracer.getSegment().parent
+                const seg = tx.trace.getParent(agent.tracer.getSegment().parentId)
                 const attributes = seg.getAttributes()
 
                 assert.ok(!err, 'no errors')
@@ -246,7 +247,7 @@ module.exports = function ({ lib, factory, poolFactory, constants }) {
             assert.ok(results && ended, 'result and end events should occur')
             const traceRoot = transaction.trace.root
             const traceRootDuration = traceRoot.timer.getDurationInMillis()
-            const segment = findSegment(traceRoot, 'Datastore/statement/MySQL/unknown/select')
+            const segment = findSegment(transaction.trace, traceRoot, 'Datastore/statement/MySQL/unknown/select')
             const queryNodeDuration = segment.timer.getDurationInMillis()
 
             assert.ok(
@@ -289,20 +290,21 @@ module.exports = function ({ lib, factory, poolFactory, constants }) {
               const transaction = agent.getTransaction().end()
               pool.release(client)
               const traceRoot = transaction.trace.root
-              const querySegment = traceRoot.children[0]
+              const [querySegment] = transaction.trace.getChildren(traceRoot.id)
+              const queryChildren = transaction.trace.getChildren(querySegment.id)
               assert.equal(
-                querySegment.children.length,
+                queryChildren.length,
                 2,
                 'the query segment should have two children'
               )
 
-              const childSegment = querySegment.children[1]
+              const childSegment = queryChildren[1]
               assert.equal(
                 childSegment.name,
                 'Callback: endCallback',
                 'children should be callbacks'
               )
-              const grandChildSegment = childSegment.children[0]
+              const [grandChildSegment] = transaction.trace.getChildren(childSegment.id)
               assert.equal(
                 grandChildSegment.name,
                 'timers.setTimeout',
@@ -373,7 +375,7 @@ module.exports = function ({ lib, factory, poolFactory, constants }) {
             client.query('use test_db;', function (err) {
               assert.ok(!err)
               client.query('SELECT 1 + 1 AS solution', function (err) {
-                const seg = agent.tracer.getSegment().parent
+                const seg = txn.trace.getParent(agent.tracer.getSegment().parentId)
                 const attributes = seg.getAttributes()
                 assert.ok(!err)
                 assert.ok(seg, 'should have a segment')
@@ -399,12 +401,3 @@ module.exports = function ({ lib, factory, poolFactory, constants }) {
     })
   })
 }
-
-function findSegment(root, segmentName) {
-  for (let i = 0; i < root.children.length; i++) {
-    const segment = root.children[i]
-    if (segment.name === segmentName) {
-      return segment
-    }
-  }
-}
diff --git a/test/versioned/mysql/pooling.js b/test/versioned/mysql/pooling.js
index 06e3ff8cbb..cca9859c9a 100644
--- a/test/versioned/mysql/pooling.js
+++ b/test/versioned/mysql/pooling.js
@@ -55,9 +55,10 @@ module.exports = function ({ factory, poolFactory, constants }) {
       const trace = transaction.trace
       plan.ok(trace, 'trace should exist')
       plan.ok(trace.root, 'root element should exist.')
-      plan.equal(trace.root.children.length, 1, 'There should be only one child.')
+      const children = trace.getChildren(trace.root.id)
+      plan.equal(children.length, 1, 'There should be only one child.')
 
-      const selectSegment = trace.root.children[0]
+      const selectSegment = children[0]
       plan.ok(selectSegment, 'trace segment for first SELECT should exist')
 
       plan.equal(
@@ -66,9 +67,12 @@ module.exports = function ({ factory, poolFactory, constants }) {
         'should register as SELECT'
       )
 
-      plan.equal(selectSegment.children.length, 1, 'should only have a callback segment')
-      plan.equal(selectSegment.children[0].name, 'Callback: <anonymous>')
-      plan.equal(selectSegment.children[0].children.length, 0)
+      const selectChildren = trace.getChildren(selectSegment.id)
+      plan.equal(selectChildren.length, 1, 'should only have a callback segment')
+      const cb = selectChildren[0]
+      plan.equal(cb.name, 'Callback: <anonymous>')
+      const cbChildren = trace.getChildren(cb.id)
+      plan.equal(cbChildren.length, 0)
     }
   })
 }
diff --git a/test/versioned/mysql2/promises.test.js b/test/versioned/mysql2/promises.test.js
index 03274bd012..a93a9b97f2 100644
--- a/test/versioned/mysql2/promises.test.js
+++ b/test/versioned/mysql2/promises.test.js
@@ -92,7 +92,7 @@ test('mysql2 promises', { timeout: 30000 }, async (t) => {
       activeTx = agent.getTransaction()
       assert.equal(tx.name, activeTx.name)
 
-      const segment = agent.getTransaction().trace.root.children[2]
+      const [, , segment] = tx.trace.getChildren(tx.trace.root.id)
       const attributes = segment.getAttributes()
       assert.equal(
         attributes.host,
diff --git a/test/versioned/nextjs/attributes.test.js b/test/versioned/nextjs/attributes.test.js
index 909fb443a7..566d97ddf3 100644
--- a/test/versioned/nextjs/attributes.test.js
+++ b/test/versioned/nextjs/attributes.test.js
@@ -10,8 +10,7 @@ const assert = require('node:assert')
 const helpers = require('./helpers')
 const nextPkg = require('next/package.json')
 const {
-  isMiddlewareInstrumentationSupported,
-  getServerSidePropsSegment
+  isMiddlewareInstrumentationSupported
 } = require('../../../lib/instrumentation/nextjs/utils')
 const middlewareSupported = isMiddlewareInstrumentationSupported(nextPkg.version)
 const agentHelper = require('../../lib/agent_helper')
@@ -199,16 +198,18 @@ test('Next.js', async (t) => {
         await helpers.makeRequest('/api/person/2?queryParam=queryValue')
         const [tx] = await txPromise
         const rootSegment = tx.trace.root
+        const [handler] = tx.trace.getChildren(rootSegment.id)
         const segments = [
           {
-            segment: rootSegment.children[0],
+            segment: handler,
             name: 'handler',
             filepath: 'pages/api/person/[id]'
           }
         ]
         if (middlewareSupported) {
+          const [middleware] = tx.trace.getChildren(handler.id)
           segments.push({
-            segment: rootSegment.children[0].children[0],
+            segment: middleware,
             name: 'middleware',
             filepath: 'middleware'
           })
@@ -231,20 +232,22 @@ test('Next.js', async (t) => {
         const [tx] = await txPromise
         const rootSegment = tx.trace.root
         const segments = []
+        const [first] = tx.trace.getChildren(rootSegment.id)
         if (middlewareSupported) {
+          const [middleware, getServerSideProps] = tx.trace.getChildren(first.id)
           segments.push({
-            segment: rootSegment.children[0].children[0],
+            segment: middleware,
             name: 'middleware',
             filepath: 'middleware'
           })
           segments.push({
-            segment: rootSegment.children[0].children[1],
+            segment: getServerSideProps,
             name: 'getServerSideProps',
             filepath: 'pages/ssr/people'
           })
         } else {
           segments.push({
-            segment: getServerSidePropsSegment(rootSegment),
+            segment: helpers.getServerSidePropsSegment(tx.trace),
             name: 'getServerSideProps',
             filepath: 'pages/ssr/people'
           })
@@ -255,8 +258,7 @@ test('Next.js', async (t) => {
           enabled,
           skipFull: true
         })
-      }
-    )
+      })
 
     await t.test('should not add CLM attrs to static page segment', async (t) => {
       agent.config.code_level_metrics = { enabled }
@@ -265,21 +267,23 @@ test('Next.js', async (t) => {
       await helpers.makeRequest('/static/dynamic/testing?queryParam=queryValue')
       const [tx] = await txPromise
       const rootSegment = tx.trace.root
+      const [root] = tx.trace.getChildren(rootSegment.id)
 
       // The segment that names the static page will not contain CLM regardless of the
       // configuration flag
       assertCLMAttrs({
-        segments: [{ segment: rootSegment.children[0] }],
+        segments: [{ segment: root }],
         enabled: false,
         skipFull: true
       })
 
       if (middlewareSupported) {
+        const [middleware] = tx.trace.getChildren(root.id)
         // this will exist when CLM is enabled
         assertCLMAttrs({
           segments: [
             {
-              segment: rootSegment.children[0].children[0],
+              segment: middleware,
               name: 'middleware',
               filepath: 'middleware'
             }
diff --git a/test/versioned/nextjs/helpers.js b/test/versioned/nextjs/helpers.js
index af38f94459..46ec34e2da 100644
--- a/test/versioned/nextjs/helpers.js
+++ b/test/versioned/nextjs/helpers.js
@@ -17,6 +17,7 @@ const noServerClose = semver.gte(nextPkg.version, '13.4.15')
 // just emit SIGTERM after 14.1.0
 const closeEvent = semver.gte(nextPkg.version, '14.1.0') ? 'SIGTERM' : 'exit'
 const { DESTINATIONS } = require('../../../lib/config/attribute-filter')
+const { findSegment } = require('../../lib/metrics_helper')
 
 /**
  * Builds a Next.js app
@@ -105,22 +106,6 @@ helpers.registerInstrumentation = function (agent) {
   hooks.forEach(agent.registerInstrumentation)
 }
 
-helpers.findSegmentByName = function (root, name) {
-  if (root.name === name) {
-    return root
-  } else if (root.children && root.children.length) {
-    for (let i = 0; i < root.children.length; i++) {
-      const child = root.children[i]
-      const found = helpers.findSegmentByName(child, name)
-      if (found) {
-        return found
-      }
-    }
-  }
-
-  return null
-}
-
 helpers.getTransactionEventAgentAttributes = function getTransactionEventAgentAttributes(
   transaction
 ) {
@@ -134,7 +119,7 @@ helpers.getTransactionIntrinsicAttributes = function getTransactionIntrinsicAttr
 }
 
 helpers.getSegmentAgentAttributes = function getSegmentAgentAttributes(transaction, name) {
-  const segment = helpers.findSegmentByName(transaction.trace.root, name)
+  const segment = findSegment(transaction.trace, transaction.trace.root, name)
   if (segment) {
     return segment.attributes.get(DESTINATIONS.SPAN_EVENT)
   }
@@ -166,3 +151,9 @@ helpers.setupTransactionHandler = function setupTransactionHandler({
     })
   })
 }
+
+helpers.getServerSidePropsSegment = function getServerSidePropsSegment(trace) {
+  const [first] = trace.getChildren(trace.root.id)
+  const children = trace.getChildren(first.id)
+  return children.find((segment) => segment.name.includes('getServerSideProps'))
+}
diff --git a/test/versioned/nextjs/segments.test.js b/test/versioned/nextjs/segments.test.js
index e3e03d320f..f0cda7dcf8 100644
--- a/test/versioned/nextjs/segments.test.js
+++ b/test/versioned/nextjs/segments.test.js
@@ -67,7 +67,7 @@ test('Next.js', async (t) => {
         children: getChildSegments(URI)
       }
     ]
-    assertSegments(tx.trace.root, expectedSegments, { exact: false })
+    assertSegments(tx.trace, tx.trace.root, expectedSegments, { exact: false })
   })
 
   await t.test('should properly name getServerSideProps segments on dynamic pages', async (t) => {
@@ -86,7 +86,7 @@ test('Next.js', async (t) => {
         children: getChildSegments(EXPECTED_URI)
       }
     ]
-    assertSegments(tx.trace.root, expectedSegments, { exact: false })
+    assertSegments(tx.trace, tx.trace.root, expectedSegments, { exact: false })
   })
 
   await t.test(
@@ -116,7 +116,7 @@ test('Next.js', async (t) => {
         ]
       }
 
-      assertSegments(tx.trace.root, expectedSegments, { exact: false })
+      assertSegments(tx.trace, tx.trace.root, expectedSegments, { exact: false })
     }
   )
 })
diff --git a/test/versioned/openai/chat-completions.test.js b/test/versioned/openai/chat-completions.test.js
index 28254d7500..52a0594922 100644
--- a/test/versioned/openai/chat-completions.test.js
+++ b/test/versioned/openai/chat-completions.test.js
@@ -65,6 +65,7 @@ test('should create span on successful chat completion create', (t, end) => {
     assert.equal(results.choices[0].message.content, '1 plus 2 is 3.')
 
     assertSegments(
+      tx.trace,
       tx.trace.root,
       [OPENAI.COMPLETION, [`External/${host}:${port}/chat/completions`]],
       { exact: false }
@@ -147,6 +148,7 @@ if (semver.gte(pkgVersion, '4.12.2')) {
       assert.equal(chunk.choices[0].message.content, res)
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         [OPENAI.COMPLETION, [`External/${host}:${port}/chat/completions`]],
         { exact: false }
@@ -361,6 +363,7 @@ if (semver.gte(pkgVersion, '4.12.2')) {
       assert.equal(events.length, 0)
       // we will still record the external segment but not the chat completion
       assertSegments(
+        tx.trace,
         tx.trace.root,
         ['timers.setTimeout', `External/${host}:${port}/chat/completions`],
         { exact: false }
diff --git a/test/versioned/openai/common.js b/test/versioned/openai/common.js
index 7e22eaaaac..deecac1fec 100644
--- a/test/versioned/openai/common.js
+++ b/test/versioned/openai/common.js
@@ -16,11 +16,12 @@ function assertChatCompletionMessages(
   { tx, chatMsgs, id, model, reqContent, resContent, tokenUsage },
   { assert = require('node:assert') } = {}
 ) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const baseMsg = {
     'appName': 'New Relic for Node.js tests',
     'request_id': '49dbbffbd3c3f4612aa48def69059aad',
     'trace_id': tx.traceId,
-    'span_id': tx.trace.root.children[0].id,
+    'span_id': segment.id,
     'response.model': model,
     'vendor': 'openai',
     'ingest_source': 'Node',
@@ -65,17 +66,18 @@ function assertChatCompletionSummary(
   { tx, model, chatSummary, error = false },
   { assert = require('node:assert') } = {}
 ) {
+  const [segment] = tx.trace.getChildren(tx.trace.root.id)
   const expectedChatSummary = {
     'id': /[a-f0-9]{36}/,
     'appName': 'New Relic for Node.js tests',
     'request_id': '49dbbffbd3c3f4612aa48def69059aad',
     'trace_id': tx.traceId,
-    'span_id': tx.trace.root.children[0].id,
+    'span_id': segment.id,
     'response.model': model,
     'vendor': 'openai',
     'ingest_source': 'Node',
     'request.model': model,
-    'duration': tx.trace.root.children[0].getDurationInMillis(),
+    'duration': segment.getDurationInMillis(),
     'response.organization': 'new-relic-nkmd8b',
     'response.headers.llmVersion': '2020-10-01',
     'response.headers.ratelimitLimitRequests': '200',
diff --git a/test/versioned/openai/embeddings.test.js b/test/versioned/openai/embeddings.test.js
index 3538f7c111..0696f7905a 100644
--- a/test/versioned/openai/embeddings.test.js
+++ b/test/versioned/openai/embeddings.test.js
@@ -61,7 +61,7 @@ test('should create span on successful embedding create', (t, end) => {
     assert.equal(results.headers, undefined, 'should remove response headers from user result')
     assert.equal(results.model, 'text-embedding-ada-002-v2')
 
-    assertSegments(tx.trace.root, [OPENAI.EMBEDDING, [`External/${host}:${port}/embeddings`]], {
+    assertSegments(tx.trace, tx.trace.root, [OPENAI.EMBEDDING, [`External/${host}:${port}/embeddings`]], {
       exact: false
     })
 
@@ -96,17 +96,18 @@ test('should create an embedding message', (t, end) => {
     const events = agent.customEventAggregator.events.toArray()
     assert.equal(events.length, 1, 'should create a chat completion message and summary event')
     const [embedding] = events
+    const [segment] = tx.trace.getChildren(tx.trace.root.id)
     const expectedEmbedding = {
       'id': /[a-f0-9]{36}/,
       'appName': 'New Relic for Node.js tests',
       'request_id': 'c70828b2293314366a76a2b1dcb20688',
       'trace_id': tx.traceId,
-      'span_id': tx.trace.root.children[0].id,
+      'span_id': segment.id,
       'response.model': 'text-embedding-ada-002-v2',
       'vendor': 'openai',
       'ingest_source': 'Node',
       'request.model': 'text-embedding-ada-002',
-      'duration': tx.trace.root.children[0].getDurationInMillis(),
+      'duration': segment.getDurationInMillis(),
       'response.organization': 'new-relic-nkmd8b',
       'token_count': undefined,
       'response.headers.llmVersion': '2020-10-01',
diff --git a/test/versioned/pg-esm/pg.common.mjs b/test/versioned/pg-esm/pg.common.mjs
index 5f5d70612c..cd64de3cd0 100644
--- a/test/versioned/pg-esm/pg.common.mjs
+++ b/test/versioned/pg-esm/pg.common.mjs
@@ -127,9 +127,14 @@ export default function runTests(name, clientFactory) {
     expect.ok(trace, 'trace should exist')
     expect.ok(trace.root, 'root element should exist')
 
-    const setSegment = findSegment(trace.root, 'Datastore/statement/Postgres/' + TABLE + '/insert')
+    const setSegment = findSegment(
+      trace,
+      trace.root,
+      'Datastore/statement/Postgres/' + TABLE + '/insert'
+    )
 
     const getSegment = findSegment(
+      trace,
       trace.root,
       'Datastore/statement/Postgres/' + selectTable + '/select'
     )
@@ -154,7 +159,11 @@ export default function runTests(name, clientFactory) {
     const agent = transaction.agent
     const trace = transaction.trace
 
-    const setSegment = findSegment(trace.root, 'Datastore/statement/Postgres/' + TABLE + '/insert')
+    const setSegment = findSegment(
+      trace,
+      trace.root,
+      'Datastore/statement/Postgres/' + TABLE + '/insert'
+    )
     const attributes = setSegment.getAttributes()
 
     const metricHostName = getMetricHostName(agent, params.postgres_host)
@@ -546,6 +555,7 @@ export default function runTests(name, clientFactory) {
               plan.ifError(error)
 
               const segment = findSegment(
+                transaction.trace,
                 transaction.trace.root,
                 'Datastore/statement/Postgres/' + TABLE + '/insert'
               )
diff --git a/test/versioned/pg/pg.common.js b/test/versioned/pg/pg.common.js
index 297b2640e3..f0971081b9 100644
--- a/test/versioned/pg/pg.common.js
+++ b/test/versioned/pg/pg.common.js
@@ -127,9 +127,14 @@ module.exports = function runTests(name, clientFactory) {
     expect.ok(trace, 'trace should exist')
     expect.ok(trace.root, 'root element should exist')
 
-    const setSegment = findSegment(trace.root, 'Datastore/statement/Postgres/' + TABLE + '/insert')
+    const setSegment = findSegment(
+      trace,
+      trace.root,
+      'Datastore/statement/Postgres/' + TABLE + '/insert'
+    )
 
     const getSegment = findSegment(
+      trace,
       trace.root,
       'Datastore/statement/Postgres/' + selectTable + '/select'
     )
@@ -154,7 +159,11 @@ module.exports = function runTests(name, clientFactory) {
     const agent = transaction.agent
     const trace = transaction.trace
 
-    const setSegment = findSegment(trace.root, 'Datastore/statement/Postgres/' + TABLE + '/insert')
+    const setSegment = findSegment(
+      trace,
+      trace.root,
+      'Datastore/statement/Postgres/' + TABLE + '/insert'
+    )
     const attributes = setSegment.getAttributes()
 
     const metricHostName = getMetricHostName(agent, params.postgres_host)
@@ -551,6 +560,7 @@ module.exports = function runTests(name, clientFactory) {
             client.query(config, [pkVal, colVal], function (error) {
               plan.ifError(error)
               const segment = findSegment(
+                transaction.trace,
                 transaction.trace.root,
                 'Datastore/statement/Postgres/' + TABLE + '/insert'
               )
diff --git a/test/versioned/prisma/prisma.test.js b/test/versioned/prisma/prisma.test.js
index ce9f557799..1e487e2ca3 100644
--- a/test/versioned/prisma/prisma.test.js
+++ b/test/versioned/prisma/prisma.test.js
@@ -58,7 +58,7 @@ test('Basic run through prisma functionality', { timeout: 30 * 1000 }, async (t)
       await helper.runInTransaction(agent, async (tx) => {
         const users = await upsertUsers(prisma)
         assert.equal(users.length, 2, 'should get two users')
-        const findManySegment = findSegment(tx.trace.root, findMany)
+        const findManySegment = findSegment(tx.trace, tx.trace.root, findMany)
         const attributes = findManySegment.getAttributes()
         assert.ok(!attributes.host, 'should not have a host set')
         assert.ok(!attributes.port_path_or_id, 'should not have a port set')
@@ -79,7 +79,7 @@ test('Basic run through prisma functionality', { timeout: 30 * 1000 }, async (t)
         const users = await query
         assert.equal(users.length, 2, 'should get two users')
         tx.end()
-        const rawSegment = findSegment(tx.trace.root, raw)
+        const rawSegment = findSegment(tx.trace, tx.trace.root, raw)
         assert.ok(rawSegment, `segment named ${raw} should exist`)
       })
     }
@@ -96,7 +96,7 @@ test('Basic run through prisma functionality', { timeout: 30 * 1000 }, async (t)
         const count = await query
         assert.equal(count, 2, 'should modify two users')
         tx.end()
-        const rawSegment = findSegment(tx.trace.root, rawUpdate)
+        const rawSegment = findSegment(tx.trace, tx.trace.root, rawUpdate)
         assert.ok(rawSegment, `segment named ${rawUpdate} should exist`)
       })
     }
diff --git a/test/versioned/prisma/utils.js b/test/versioned/prisma/utils.js
index 25c6b66469..65caea1723 100644
--- a/test/versioned/prisma/utils.js
+++ b/test/versioned/prisma/utils.js
@@ -59,10 +59,10 @@ function verifyTraces(agent, transaction) {
   assert.ok(trace, 'trace should exist')
   assert.ok(trace.root, 'root element should exist')
 
-  assertSegments(trace.root, [findMany, update, update, findMany], { exact: true })
-  const findManySegment = findSegment(trace.root, findMany)
+  assertSegments(trace, trace.root, [findMany, update, update, findMany], { exact: true })
+  const findManySegment = findSegment(trace, trace.root, findMany)
   assert.ok(findManySegment.timer.hrDuration, 'findMany segment should have ended')
-  const updateSegment = findSegment(trace.root, update)
+  const updateSegment = findSegment(trace, trace.root, update)
   assert.ok(updateSegment.timer.hrDuration, 'update segment should have ended')
   for (const segment of [findManySegment, updateSegment]) {
     const attributes = segment.getAttributes()
diff --git a/test/versioned/q/q.test.js b/test/versioned/q/q.test.js
index daa7eaa416..b78a190fe3 100644
--- a/test/versioned/q/q.test.js
+++ b/test/versioned/q/q.test.js
@@ -15,7 +15,8 @@ const helper = require('../../lib/agent_helper')
 
 function assertTransaction(agent, tx, expect = assert) {
   expect.equal(agent.getTransaction(), tx)
-  expect.equal(agent.getTransaction().trace.root.children.length, 0)
+  const children = tx.trace.getChildren(tx.trace.root.id)
+  expect.equal(children.length, 0)
 }
 
 test.beforeEach((ctx) => {
diff --git a/test/versioned/redis/redis-v4-legacy-mode.test.js b/test/versioned/redis/redis-v4-legacy-mode.test.js
index 060e6c23b0..441df30882 100644
--- a/test/versioned/redis/redis-v4-legacy-mode.test.js
+++ b/test/versioned/redis/redis-v4-legacy-mode.test.js
@@ -75,16 +75,17 @@ test('Redis instrumentation', async function (t) {
       const trace = transaction.trace
       assert.ok(trace, 'trace should exist')
       assert.ok(trace.root, 'root element should exist')
-      assert.equal(trace.root.children.length, 2, 'there should be only two children of the root')
+      const children = trace.getChildren(trace.root.id)
+      assert.equal(children.length, 2, 'there should be only two children of the root')
 
-      const setSegment = trace.root.children[0]
+      const [setSegment, getSegment] = children
       const setAttributes = setSegment.getAttributes()
       assert.ok(setSegment, 'trace segment for set should exist')
       assert.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set')
       assert.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute')
-      assert.equal(setSegment.children.length, 0, 'set should have no children')
+      const setSegmentChildren = trace.getChildren(setSegment.id)
+      assert.equal(setSegmentChildren.length, 0, 'set should have no children')
 
-      const getSegment = trace.root.children[1]
       const getAttributes = getSegment.getAttributes()
       assert.ok(getSegment, 'trace segment for get should exist')
 
@@ -124,10 +125,10 @@ test('Redis instrumentation', async function (t) {
     assert.ok(!agent.getTransaction(), 'no transaction should be in play')
     agent.config.attributes.enabled = true
 
-    helper.runInTransaction(agent, async function () {
+    helper.runInTransaction(agent, async function (tx) {
       await client.v4.set('saveme', 'foobar')
 
-      const segment = agent.tracer.getSegment().children[0]
+      const [segment] = tx.trace.getChildren(agent.tracer.getSegment().id)
       assert.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute')
       end()
     })
@@ -138,10 +139,10 @@ test('Redis instrumentation', async function (t) {
     assert.ok(!agent.getTransaction(), 'no transaction should be in play')
     agent.config.attributes.enabled = false
 
-    helper.runInTransaction(agent, async function () {
+    helper.runInTransaction(agent, async function (tx) {
       await client.v4.set('saveme', 'foobar')
 
-      const segment = agent.tracer.getSegment().children[0]
+      const [segment] = tx.trace.getChildren(agent.tracer.getSegment().id)
       assert.ok(!segment.getAttributes().key, 'should not have `key` attribute')
       end()
     })
@@ -159,7 +160,7 @@ test('Redis instrumentation', async function (t) {
       await client.v4.set('testkey', 'arglbargle')
 
       const trace = transaction.trace
-      const setSegment = trace.root.children[0]
+      const [setSegment] = trace.getChildren(trace.root.id)
       const attributes = setSegment.getAttributes()
       assert.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute')
       assert.equal(
@@ -188,7 +189,7 @@ test('Redis instrumentation', async function (t) {
       const transaction = agent.getTransaction()
       await client.v4.set('testkey', 'arglbargle')
 
-      const setSegment = transaction.trace.root.children[0]
+      const [setSegment] = transaction.trace.getChildren(transaction.trace.root.id)
       const attributes = setSegment.getAttributes()
       assert.equal(attributes.host, undefined, 'should not have host attribute')
       assert.equal(attributes.port_path_or_id, undefined, 'should not have port attribute')
@@ -226,9 +227,9 @@ test('Redis instrumentation', async function (t) {
     })
 
     function verify() {
-      const setSegment1 = transaction.trace.root.children[0]
-      const selectSegment = transaction.trace.root.children[1]
-      const setSegment2 = transaction.trace.root.children[2]
+      const [setSegment1, selectSegment, setSegment2] = transaction.trace.getChildren(
+        transaction.trace.root.id
+      )
 
       assert.equal(
         setSegment1.name,
diff --git a/test/versioned/redis/redis-v4.test.js b/test/versioned/redis/redis-v4.test.js
index 830acef392..9db7190ef6 100644
--- a/test/versioned/redis/redis-v4.test.js
+++ b/test/versioned/redis/redis-v4.test.js
@@ -74,16 +74,16 @@ test('Redis instrumentation', async function (t) {
       const trace = transaction.trace
       assert.ok(trace, 'trace should exist')
       assert.ok(trace.root, 'root element should exist')
-      assert.equal(trace.root.children.length, 2, 'there should be only two children of the root')
+      const children = trace.getChildren(trace.root.id)
+      assert.equal(children.length, 2, 'there should be only two children of the root')
 
-      const setSegment = trace.root.children[0]
+      const [setSegment, getSegment] = children
       const setAttributes = setSegment.getAttributes()
       assert.ok(setSegment, 'trace segment for set should exist')
       assert.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set')
       assert.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute')
-      assert.equal(setSegment.children.length, 0, 'set should have no children')
-
-      const getSegment = trace.root.children[1]
+      const setSegmentChildren = trace.getChildren(setSegment.id)
+      assert.equal(setSegmentChildren.length, 0, 'set should have no children')
       const getAttributes = getSegment.getAttributes()
       assert.ok(getSegment, 'trace segment for get should exist')
 
@@ -148,10 +148,10 @@ test('Redis instrumentation', async function (t) {
     assert.ok(!agent.getTransaction(), 'no transaction should be in play')
     agent.config.attributes.enabled = true
 
-    helper.runInTransaction(agent, async function () {
+    helper.runInTransaction(agent, async function (tx) {
       await client.set('saveme', 'foobar')
 
-      const segment = agent.tracer.getSegment().children[0]
+      const [segment] = tx.trace.getChildren(agent.tracer.getSegment().id)
       assert.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute')
       end()
     })
@@ -162,10 +162,10 @@ test('Redis instrumentation', async function (t) {
     assert.ok(!agent.getTransaction(), 'no transaction should be in play')
     agent.config.attributes.enabled = false
 
-    helper.runInTransaction(agent, async function () {
+    helper.runInTransaction(agent, async function (tx) {
       await client.set('saveme', 'foobar')
 
-      const segment = agent.tracer.getSegment().children[0]
+      const [segment] = tx.trace.getChildren(agent.tracer.getSegment().id)
       assert.ok(!segment.getAttributes().key, 'should not have `key` attribute')
       end()
     })
@@ -183,7 +183,7 @@ test('Redis instrumentation', async function (t) {
       await client.set('testkey', 'arglbargle')
 
       const trace = transaction.trace
-      const setSegment = trace.root.children[0]
+      const [setSegment] = trace.getChildren(trace.root.id)
       const attributes = setSegment.getAttributes()
       assert.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute')
       assert.equal(
@@ -212,7 +212,7 @@ test('Redis instrumentation', async function (t) {
       const transaction = agent.getTransaction()
       await client.set('testkey', 'arglbargle')
 
-      const setSegment = transaction.trace.root.children[0]
+      const [setSegment] = transaction.trace.getChildren(transaction.trace.root.id)
       const attributes = setSegment.getAttributes()
       assert.equal(attributes.host, undefined, 'should not have host attribute')
       assert.equal(attributes.port_path_or_id, undefined, 'should not have port attribute')
@@ -250,9 +250,9 @@ test('Redis instrumentation', async function (t) {
     })
 
     function verify() {
-      const setSegment1 = transaction.trace.root.children[0]
-      const selectSegment = transaction.trace.root.children[1]
-      const setSegment2 = transaction.trace.root.children[2]
+      const [setSegment1, selectSegment, setSegment2] = transaction.trace.getChildren(
+        transaction.trace.root.id
+      )
 
       assert.equal(
         setSegment1.name,
diff --git a/test/versioned/redis/redis.test.js b/test/versioned/redis/redis.test.js
index 002a596623..e348c6fdd2 100644
--- a/test/versioned/redis/redis.test.js
+++ b/test/versioned/redis/redis.test.js
@@ -75,16 +75,17 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
           const trace = transaction.trace
           plan.ok(trace, 'trace should exist')
           plan.ok(trace.root, 'root element should exist')
-          plan.equal(trace.root.children.length, 1, 'there should be only one child of the root')
-
-          const setSegment = trace.root.children[0]
+          const children = trace.getChildren(trace.root.id)
+          plan.equal(children.length, 1, 'there should be only one child of the root')
+          const [setSegment] = children
           const setAttributes = setSegment.getAttributes()
           plan.ok(setSegment, 'trace segment for set should exist')
           plan.equal(setSegment.name, 'Datastore/operation/Redis/set', 'should register the set')
           plan.equal(setAttributes.key, '"testkey"', 'should have the set key as a attribute')
-          plan.equal(setSegment.children.length, 1, 'set should have an only child')
-
-          const getSegment = setSegment.children[0].children[0]
+          const setChildren = trace.getChildren(setSegment.id)
+          plan.equal(setChildren.length, 1, 'set should have an only child')
+          const [getSegment] = trace.getChildren(setChildren[0].id)
+          const getChildren = trace.getChildren(getSegment.id)
           const getAttributes = getSegment.getAttributes()
           plan.ok(getSegment, 'trace segment for get should exist')
 
@@ -92,7 +93,7 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
 
           plan.equal(getAttributes.key, '"testkey"', 'should have the get key as a attribute')
 
-          plan.ok(getSegment.children.length >= 1, 'get should have a callback segment')
+          plan.ok(getChildren.length >= 1, 'get should have a callback segment')
 
           plan.ok(getSegment.timer.hrDuration, 'trace segment should have ended')
         })
@@ -142,13 +143,14 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
     })
 
     agent.on('transactionFinished', function (tx) {
-      const redSeg = tx.trace.root.children[0]
+      const [redSeg] = tx.trace.getChildren(tx.trace.root.id)
       plan.equal(
         redSeg.name,
         'Datastore/operation/Redis/set',
         'should have untruncated redis segment'
       )
-      plan.equal(redSeg.children.length, 0, 'should have no children for redis segment')
+      const redChildren = tx.trace.getChildren(redSeg.id)
+      plan.equal(redChildren.length, 0, 'should have no children for redis segment')
     })
     await plan.completed
   })
@@ -214,11 +216,11 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
     const { agent, client } = t.nr
     agent.config.attributes.enabled = true
 
-    helper.runInTransaction(agent, function () {
+    helper.runInTransaction(agent, function (tx) {
       client.set('saveme', 'foobar', function (error) {
         // Regardless of error, key should still be captured.
         assert.ok(!error)
-        const segment = agent.tracer.getSegment().parent
+        const segment = tx.trace.getParent(agent.tracer.getSegment().parentId)
         assert.equal(segment.getAttributes().key, '"saveme"', 'should have `key` attribute')
         end()
       })
@@ -229,11 +231,11 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
     const { agent, client } = t.nr
     agent.config.attributes.enabled = false
 
-    helper.runInTransaction(agent, function () {
+    helper.runInTransaction(agent, function (tx) {
       client.set('saveme', 'foobar', function (error) {
         // Regardless of error, key should still be captured.
         assert.ok(!error)
-        const segment = agent.tracer.getSegment().parent
+        const segment = tx.trace.getParent(agent.tracer.getSegment().parentId)
         assert.ok(!segment.getAttributes().key, 'should not have `key` attribute')
         end()
       })
@@ -252,7 +254,7 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
       client.set('testkey', 'arglbargle', function (error) {
         plan.ok(!error)
         const trace = transaction.trace
-        const setSegment = trace.root.children[0]
+        const [setSegment] = trace.getChildren(trace.root.id)
         const attributes = setSegment.getAttributes()
         plan.equal(attributes.host, METRIC_HOST_NAME, 'should have host as attribute')
         plan.equal(
@@ -282,7 +284,7 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
       const transaction = agent.getTransaction()
       client.set('testkey', 'arglbargle', function (error) {
         plan.ok(!error)
-        const setSegment = transaction.trace.root.children[0]
+        const [setSegment] = transaction.trace.getChildren(transaction.trace.root.id)
         const attributes = setSegment.getAttributes()
         plan.equal(attributes.host, undefined, 'should not have host attribute')
         plan.equal(attributes.port_path_or_id, undefined, 'should not have port attribute')
@@ -327,9 +329,13 @@ test('Redis instrumentation', { timeout: 20000 }, async function (t) {
     await plan.completed
 
     function verify() {
-      const setSegment1 = transaction.trace.root.children[0]
-      const selectSegment = setSegment1.children[0].children[0]
-      const setSegment2 = selectSegment.children[0].children[0]
+      const [setSegment1] = transaction.trace.getChildren(transaction.trace.root.id)
+      const [selectSegment] = transaction.trace.getChildren(
+        transaction.trace.getChildren(setSegment1.id)[0].id
+      )
+      const [setSegment2] = transaction.trace.getChildren(
+        transaction.trace.getChildren(selectSegment.id)[0].id
+      )
 
       plan.equal(setSegment1.name, 'Datastore/operation/Redis/set', 'should register the first set')
       plan.equal(
diff --git a/test/versioned/restify/router.test.js b/test/versioned/restify/router.test.js
index 365a7cc6ec..9684a5c996 100644
--- a/test/versioned/restify/router.test.js
+++ b/test/versioned/restify/router.test.js
@@ -55,7 +55,7 @@ test('Restify router', async function (t) {
       plan.equal(transaction.verb, 'GET', 'HTTP method is GET')
       plan.ok(transaction.trace, 'transaction has trace')
 
-      const web = transaction.trace.root.children[0]
+      const [web] = transaction.trace.getChildren(transaction.trace.root.id)
       plan.ok(web, 'trace has web segment')
       plan.equal(web.name, transaction.name, 'segment name and transaction name match')
       plan.equal(web.partialName, 'Restify/GET//test/:id', 'should have partial name for apdex')
diff --git a/test/versioned/superagent/async-await.test.js b/test/versioned/superagent/async-await.test.js
index 55f54e7a72..edd706361c 100644
--- a/test/versioned/superagent/async-await.test.js
+++ b/test/versioned/superagent/async-await.test.js
@@ -41,11 +41,12 @@ test('should maintain transaction context with promises', (t, end) => {
     const { request } = t.nr
     await request.get(address)
 
-    const mainSegment = tx.trace.root.children[0]
+    const [mainSegment] = tx.trace.getChildren(tx.trace.root.id)
     assert.ok(mainSegment)
     match(mainSegment.name, EXTERNAL_NAME, 'has segment matching request')
+    const mainChildren = tx.trace.getChildren(mainSegment.id)
     assert.equal(
-      mainSegment.children.filter((c) => c.name === 'Callback: <anonymous>').length,
+      mainChildren.filter((c) => c.name === 'Callback: <anonymous>').length,
       1,
       'CB created by superagent is present'
     )
diff --git a/test/versioned/superagent/superagent.test.js b/test/versioned/superagent/superagent.test.js
index 481135cd9a..970bf56544 100644
--- a/test/versioned/superagent/superagent.test.js
+++ b/test/versioned/superagent/superagent.test.js
@@ -40,11 +40,12 @@ test('should maintain transaction context with callbacks', (t, end) => {
     request.get(address, function testCallback() {
       assert.ok(tx)
 
-      const mainSegment = tx.trace.root.children[0]
+      const [mainSegment] = tx.trace.getChildren(tx.trace.root.id)
       assert.ok(mainSegment)
       match(mainSegment.name, EXTERNAL_NAME, 'has segment matching request')
+      const mainChildren = tx.trace.getChildren(mainSegment.id)
       assert.equal(
-        mainSegment.children.filter((c) => c.name === 'Callback: testCallback').length,
+        mainChildren.filter((c) => c.name === 'Callback: testCallback').length,
         1,
         'has segment matching callback'
       )
@@ -68,11 +69,12 @@ test('should maintain transaction context with promises', (t, end) => {
     request.get(address).then(function testThen() {
       assert.ok(tx)
 
-      const mainSegment = tx.trace.root.children[0]
+      const [mainSegment] = tx.trace.getChildren(tx.trace.root.id)
       assert.ok(mainSegment)
       match(mainSegment.name, EXTERNAL_NAME, 'has segment matching request')
+      const mainChildren = tx.trace.getChildren(mainSegment.id)
       assert.equal(
-        mainSegment.children.filter((c) => c.name === 'Callback: <anonymous>').length,
+        mainChildren.filter((c) => c.name === 'Callback: <anonymous>').length,
         1,
         'has segment matching callback'
       )
diff --git a/test/versioned/undici/requests.test.js b/test/versioned/undici/requests.test.js
index d4987fbc1b..9932ec44c1 100644
--- a/test/versioned/undici/requests.test.js
+++ b/test/versioned/undici/requests.test.js
@@ -82,7 +82,7 @@ test('Undici request tests', async (t) => {
       })
       assert.equal(statusCode, 200)
 
-      assertSegments(tx.trace.root, [`External/${HOST}/post`], { exact: false })
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/post`], { exact: false })
       tx.end()
     })
   })
@@ -115,7 +115,7 @@ test('Undici request tests', async (t) => {
 
       await client.request({ path: '/', method: 'GET' })
 
-      assertSegments(transaction.trace.root, [`External/localhost:${port}/`], {
+      assertSegments(transaction.trace, transaction.trace.root, [`External/localhost:${port}/`], {
         exact: false
       })
 
@@ -130,7 +130,7 @@ test('Undici request tests', async (t) => {
         method: 'GET'
       })
       assert.equal(statusCode, 200)
-      const segment = metrics.findSegment(tx.trace.root, `External/${HOST}/get`)
+      const segment = metrics.findSegment(tx.trace, tx.trace.root, `External/${HOST}/get`)
       const attrs = segment.getAttributes()
       assert.equal(attrs.url, `${REQUEST_URL}/get`)
       assert.equal(attrs.procedure, 'GET')
@@ -194,7 +194,7 @@ test('Undici request tests', async (t) => {
       const [{ statusCode }, { statusCode: statusCode2 }] = await Promise.all([req1, req2])
       assert.equal(statusCode, 200)
       assert.equal(statusCode2, 200)
-      assertSegments(tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], {
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/post`, `External/${HOST}/put`], {
         exact: false
       })
       tx.end()
@@ -210,7 +210,7 @@ test('Undici request tests', async (t) => {
         })
       } catch (err) {
         assert.ok(err)
-        assertSegments(tx.trace.root, ['External/invalidurl/foo'], { exact: false })
+        assertSegments(tx.trace, tx.trace.root, ['External/invalidurl/foo'], { exact: false })
         assert.equal(tx.exceptions.length, 1)
         tx.end()
       }
@@ -230,7 +230,7 @@ test('Undici request tests', async (t) => {
         }, 100)
         await req
       } catch (err) {
-        assertSegments(tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false })
+        assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/delay/1000`], { exact: false })
         assert.equal(tx.exceptions.length, 1)
         const expectedErrMsg = semver.gte(pkgVersion, '6.3.0')
           ? 'This operation was aborted'
@@ -259,11 +259,11 @@ test('Undici request tests', async (t) => {
       try {
         await req
       } catch (error) {
-        assertSegments(transaction.trace.root, [`External/localhost:${port}/`], {
+        assertSegments(transaction.trace, transaction.trace.root, [`External/localhost:${port}/`], {
           exact: false
         })
 
-        const segments = transaction.trace.root.children
+        const segments = transaction.trace.getChildren(transaction.trace.root.id)
         const segment = segments[segments.length - 1]
 
         assert.ok(segment.timer.start, 'should have started')
@@ -281,7 +281,7 @@ test('Undici request tests', async (t) => {
         method: 'GET'
       })
       assert.equal(statusCode, 400)
-      assertSegments(tx.trace.root, [`External/${HOST}/status/400`], { exact: false })
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/status/400`], { exact: false })
       tx.end()
     })
   })
@@ -290,7 +290,7 @@ test('Undici request tests', async (t) => {
     await helper.runInTransaction(agent, async (tx) => {
       const res = await undici.fetch(REQUEST_URL)
       assert.equal(res.status, 200)
-      assertSegments(tx.trace.root, [`External/${HOST}/`], { exact: false })
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/`], { exact: false })
       tx.end()
     })
   })
@@ -312,7 +312,7 @@ test('Undici request tests', async (t) => {
           })
         }
       )
-      assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false })
+      assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/get`], { exact: false })
       tx.end()
     })
   })
@@ -347,7 +347,7 @@ test('Undici request tests', async (t) => {
         }),
         (err) => {
           assert.ok(!err)
-          assertSegments(tx.trace.root, [`External/${HOST}/get`], { exact: false })
+          assertSegments(tx.trace, tx.trace.root, [`External/${HOST}/get`], { exact: false })
           tx.end()
           end()
         }
diff --git a/test/versioned/when/segments.test.js b/test/versioned/when/segments.test.js
index 2de2e146ca..1b3b293491 100644
--- a/test/versioned/when/segments.test.js
+++ b/test/versioned/when/segments.test.js
@@ -60,9 +60,11 @@ test('segments enabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', (tx) => {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         [
           'doSomeWork',
@@ -97,8 +99,11 @@ test('segments enabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
+
       assertSegments(
+        tx.trace,
         tx.trace.root,
         [
           'doWork1',
@@ -140,9 +145,11 @@ test('segments enabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         [
           'doWork1',
@@ -171,9 +178,11 @@ test('segments enabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         ['doWork1', ['Promise startSomeWork', ['Promise#catch catchHandler']]],
         {},
@@ -199,9 +208,11 @@ test('segments enabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         [
           'doWork1',
@@ -252,9 +263,11 @@ test('segments enabled', async (t) => {
     const { Promise } = when
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 2)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 2)
 
       assertSegments(
+        tx.trace,
         tx.trace.root,
         ['Promise startSomeWork', ['Promise#then myThen'], 'doSomeWork'],
         { exact: true },
@@ -307,9 +320,10 @@ test('segments disabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
-      assertSegments(tx.trace.root, ['doSomeWork', ['someChildSegment']], {}, { assert: plan })
+      assertSegments(tx.trace, tx.trace.root, ['doSomeWork', ['someChildSegment']], {}, { assert: plan })
     })
 
     helper.runInTransaction(agent, function transactionWrapper(transaction) {
@@ -336,9 +350,10 @@ test('segments disabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
-      assertSegments(tx.trace.root, ['doWork1'], {}, { assert: plan })
+      assertSegments(tx.trace, tx.trace.root, ['doWork1'], {}, { assert: plan })
     })
 
     helper.runInTransaction(agent, function transactionWrapper(transaction) {
@@ -361,9 +376,10 @@ test('segments disabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
-      assertSegments(tx.trace.root, ['doWork1'], {}, { assert: plan })
+      assertSegments(tx.trace, tx.trace.root, ['doWork1'], {}, { assert: plan })
     })
 
     helper.runInTransaction(agent, function transactionWrapper(transaction) {
@@ -384,9 +400,10 @@ test('segments disabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
-      assertSegments(tx.trace.root, ['doWork1'], {}, { assert: plan })
+      assertSegments(tx.trace, tx.trace.root, ['doWork1'], {}, { assert: plan })
     })
 
     helper.runInTransaction(agent, function transactionWrapper(transaction) {
@@ -407,9 +424,10 @@ test('segments disabled', async (t) => {
     const { agent, tracer, when } = t.nr
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
-      assertSegments(tx.trace.root, ['doWork1', ['doWork2']], {}, { assert: plan })
+      assertSegments(tx.trace, tx.trace.root, ['doWork1', ['doWork2']], {}, { assert: plan })
     })
 
     helper.runInTransaction(agent, function transactionWrapper(transaction) {
@@ -437,9 +455,10 @@ test('segments disabled', async (t) => {
     const { Promise } = when
 
     agent.once('transactionFinished', function (tx) {
-      plan.equal(tx.trace.root.children.length, 1)
+      const children = tx.trace.getChildren(tx.trace.root.id)
+      plan.equal(children.length, 1)
 
-      assertSegments(tx.trace.root, ['doSomeWork'], { exact: true }, { assert: plan })
+      assertSegments(tx.trace, tx.trace.root, ['doSomeWork'], { exact: true }, { assert: plan })
     })
 
     helper.runInTransaction(agent, function transactionWrapper(transaction) {