A website copies a quantity into an existing model and its setter records a change. Rewriting the expression with object spread produces an object that looks similar, yet the setter no longer runs. The difference is not cosmetic: one operation assigns into a target, while the other creates data properties in a new object literal.
The short answer is that ordinary Object.assign reads eligible source values and uses assignment semantics on its target. Those writes can invoke target setters. Object spread in an object literal reads eligible source values but creates data properties on the object being constructed. It does not promise to replay an existing model's setter behaviour.
This guide uses original stock-card and form-state fixtures, then changes descriptors, prototypes and failure points. The examples concern controlled ordinary objects with standard built-ins. They are teaching models, not a claim about a particular framework, database transaction or arbitrary proxy.
This specialist guide belongs to the Websites root. It also connects to The Gold Standard of Education | Learning That Transfers into Life and the How X Works hub. The worked cases are original teaching examples; their numbers are stipulated so that the reasoning can be checked.
eduKateSG · Websites
Find your next learning step
Trace source reads and destination writes separately. Compare target setters, literal data properties, inherited descriptors, partial changes and shallow references.
ROUTE 1 · CHAPTERS 1–3
Separate assignment from construction
Follow a target setter and a fresh literal record.
ROUTE 2 · CHAPTERS 4–6
Inspect descriptors and reads
Check inherited setters, property shape and source getters.
ROUTE 3 · CHAPTERS 7–9
Trace order and failure
Work multiple sources, eligible keys and partial writes.
ROUTE 4 · CHAPTERS 10–11
Apply the data contract
Check nested references and quantity-entry validation.
ROUTE 5 · CHAPTERS 12–14
Test and choose a boundary
Verify fresh fixtures and describe the intended update.
Chapter contents
Separate assignment from construction · Chapters 1–3
1. Separate the source read from the destination write
Inspect descriptors and reads · Chapters 4–6
4. Follow a setter inherited through the prototype
Trace order and failure · Chapters 7–9
7. Trace repeated writes from multiple sources
Apply the data contract · Chapters 10–11
10. Do not mistake a shallow record for an independent model
Test and choose a boundary · Chapters 12–14
12. Verify the two destination contracts with fresh fixtures
CHAPTER 1 OF 14 · Separate assignment from construction
1. Separate the source read from the destination write
Start with a source record containing a quantity of seven and a label of Cedar. A copying expression must obtain each eligible value from that source. It must also decide how to place the value at its destination. These are different stages, and a mechanism can agree at the first stage while differing at the second.
Let the destination be an existing stock model. Its quantity property is an accessor: assigning a quantity calls a setter that records the supplied value and updates a backing variable. Reading quantity calls a getter that returns the backing variable. The visible property is therefore an interface to behaviour, not simply a storage slot.
Another possible destination is a fresh plain record. Its quantity property directly stores a value. Reading or writing that ordinary data property does not invoke the model's quantity accessor because that accessor is not the property being used. Similar field names do not establish shared behaviour.
Before comparing expressions, name source, existing model and newly constructed result separately. Otherwise the phrase copied object can conceal which identity changed and which one a later renderer reads. A successful expression returning some object does not establish that it updated the particular object already held by the interface.
For the first ledger, keep four fields: source quantity, destination identity, destination property kind and recorded setter calls. The source quantity alone cannot predict the final behaviour. You need the destination's current property contract and the kind of write the copying mechanism performs.
This distinction is the foundation for the rest of the lesson. Source getters explain how obtaining a value can execute code. Target setters explain how placing a value can execute code. The connected structured-cloning guide develops source getter effects; here we concentrate on the destination side that a copying rewrite can silently change.
CHAPTER 2 OF 14 · Separate assignment from construction
2. Trace assignment into a target-owned setter
Construct a fresh model with a local backing quantity initially zero. Its setter appends every supplied quantity to a calls array before storing the value. Apply Object.assign(model, { quantity: 7 }). The returned object is the model itself, the setter receives seven, and the backing quantity becomes seven.
The source contains an ordinary data property, so no source getter is needed in this fixture. The observable behaviour comes from the destination write. This is useful isolation: a counter on both source and target would make the first example harder to diagnose than necessary.
const calls = [];
let stored = 0;
const model = {
get quantity() { return stored; },
set quantity(value) { calls.push(value); stored = value; }
};
const result = Object.assign(model, { quantity: 7 });
// result === model; calls contains 7; model.quantity is 7.
The target's accessor descriptor remains an accessor after this ordinary assignment. The copying call has not replaced it with a new data descriptor. A later assignment to model.quantity can therefore invoke the same setter again under its normal property rules.
Change the setter so it stores twice the supplied quantity. Assigning seven now makes the getter return fourteen, while the recorded input remains seven. The source value, setter input and later observable model value are three distinct observations. Calling all of them the copied value would obscure the model's deliberate transformation.
For a practical check, inspect both calls and the property descriptor. A final getter value of seven alone would not prove that the setter ran; a plain data property could print the same value. The behavioural evidence is the recorded invocation together with the unchanged accessor descriptor and target identity.
CHAPTER 3 OF 14 · Separate assignment from construction
3. Build a new record with object spread
Now construct const record = { ...source } from a source whose quantity is seven. The result is a new object with an ordinary data property holding seven. There is no existing stock-model setter at that destination, so the model's calls array remains unchanged in a separate controlled comparison.
This comparison changes more than the spelling of the copying operation. Object.assign was given a particular existing target; the object literal constructs a different identity. If a component still holds model, creating record does not cause that component's reference to point to record. The caller must deliberately decide how a new record enters the application.
The distinction also applies to a literal with an accessor declared before the spread. In the controlled ordinary literal, a later spread of an eligible quantity creates a data property of that name, replacing the earlier configurable literal accessor. It does not call that earlier setter as an assignment would.
const calls = [];
const record = {
set quantity(value) { calls.push(value); },
...{ quantity: 7 }
};
// calls stays empty; quantity is an own data property holding 7.
Check the descriptor rather than relying only on record.quantity. The returned value is seven in both the simple assignment fixture and this literal fixture. One reached a target setter and retained an accessor; the other constructed a data property. Equal readback values do not establish equivalent update behaviour.
Change the literal order so the setter is declared after the spread. The later definition now determines the property shape in that literal. This is an order-of-definitions case, not evidence that spread invoked the setter. Keep construction order distinct from a later explicit assignment to the completed object's property.
There is another misleading rewrite: spreading the model itself before spreading the update. Reading the model's enumerable quantity getter can obtain its current value, but that read does not carry the getter and setter pair into the new record. A later quantity from the update defines another data value there. The model accessor has participated as a source interface, not been installed as the destination interface.
Use different counters for getter reads and setter writes in that comparison. One recorded getter read alongside zero setter calls is a coherent result. A single counter labelled property activity would conceal which direction produced the effect and could make the rewrite appear to preserve behaviour that it actually removed.
CHAPTER 4 OF 14 · Inspect descriptors and reads
4. Follow a setter inherited through the prototype
Target setters need not be declared directly on the target. Build an ordinary prototype with a quantity setter, then create a model inheriting from it. When Object.assign writes quantity on that model, ordinary assignment can find and invoke the inherited setter rather than simply creating an own quantity data property.
Use a setter that writes this._quantity and records its receiver. The receiver is the model in this fixture. The model can gain its own backing _quantity field while quantity remains supplied by the prototype accessor. Inspecting only own quantity properties would miss the mechanism responsible for the change.
This explains a common diagnostic surprise: the target appears not to contain a setter in its own descriptors, yet a copy triggers behaviour. The relevant ordinary assignment lookup can involve the prototype chain. State which property is being written and where its controlling descriptor is found.
Compare a fresh spread record constructed from the source. The new literal uses its own data-property creation path and does not inherit the model prototype merely because the source values came from a model-related record. Matching quantity field names do not transfer that prototype relationship.
A second fixture uses a prototype with a non-writable quantity data property instead of a setter. Ordinary assignment to that name can fail rather than shadowing it as a new own field. This is a different descriptor condition and deserves a separate expectation; inherited does not always mean setter.
Keep the claims within ordinary objects. Proxies can intercept operations, and platform objects can have specialised contracts. The prototype fixture teaches why a target-owned descriptor check can be incomplete. It does not establish that every target's behaviour can be predicted from one printed property list or one successful test on a plain literal.
CHAPTER 5 OF 14 · Inspect descriptors and reads
5. Compare property descriptors after the copy
A property descriptor tells you whether quantity stores a value or exposes get and set functions. For an ordinary data property it also describes writable, enumerable and configurable attributes. These attributes matter when future writes or inspection are part of the application's contract.
Give an existing target a writable quantity data property that is non-enumerable and non-configurable. Object.assign can update its stored value under the stated writable condition while preserving those existing attributes. It does not generally replace the destination descriptor with the source descriptor just because a value was copied.
By contrast, an eligible source value spread into a fresh plain literal becomes a normally created data property. In the controlled fixture that property is writable, enumerable and configurable. The source's descriptor attributes have not been copied wholesale, and the previous target's special attributes are irrelevant to the new object's construction.
For the ledger, record source descriptor, target-before descriptor and destination-after descriptor separately. A source non-enumerable property is ordinarily omitted from these value-copying paths, whereas an existing non-enumerable target property can still receive an assignment from an eligible enumerable source property with the same name.
That last comparison prevents a subtle confusion. Source enumerability selects which values are considered. Target enumerability does not by itself determine whether assignment may update a matching target property. Selection at the source and permission at the target are different questions.
Use descriptor inspection as a diagnostic for known ordinary fixtures, not as a universal side-effect-free inspection promise for every possible object. The useful conclusion is bounded: under these stated descriptors, the two mechanisms leave different property shapes that can affect later behaviour even when their immediate printed values match.
CHAPTER 6 OF 14 · Inspect descriptors and reads
6. Keep source getter execution visible in both mechanisms
Changing the destination operation does not remove the need to read source values. If the source has an eligible enumerable own getter for quantity, both Object.assign and object spread can invoke that getter while obtaining the value. Avoid advertising spread as a general way to copy without executing source code.
Use fresh source fixtures with separate counters. The getter increments its counter and returns seven. First assign into a target whose setter records seven. The resulting trace contains a source read followed by a target setter call. A second fresh fixture spreads into a literal and records the source read without that target setter call.
These traces isolate the difference. Both obtain a source value; their destination writes have different semantics. If a test reuses one getter-bearing source for several operations, later counter values include earlier reads. Reset the fixture or preserve a complete labelled sequence before attributing effects to one mechanism.
The structured-cloning getter article follows additions, deletions, exceptions and graph traversal in depth. Those are separate serialization questions. This lesson needs only the source-read boundary necessary to explain why avoiding a destination setter does not establish an effect-free copy.
For a stock form, a computed quantity might read a changing store. Copying its current return value into a plain record can be useful, but the producer still owns the consistency of that value. The copying spelling does not validate stock availability or establish a simultaneous snapshot across all fields.
The practical check asks two questions in order: what executes when this source value is obtained, and what executes when that value is placed at this destination? Answering only one leaves half the behaviour unexplained. Naming both stages turns an apparently mysterious copy into a sequence that can be tested.
CHAPTER 7 OF 14 · Trace order and failure
7. Trace repeated writes from multiple sources
Object.assign can process several source objects in order. Give the target its recording quantity setter, then provide a first source with quantity seven and a second source with quantity eleven. The setter can record both writes. The final observable quantity alone does not reveal that the earlier setter invocation happened.
This matters when setter calls do more than store a value. They might record local changes, update derived fields or enforce a model rule. Even if the later source overrides the value, an earlier observable effect is not automatically erased. A last-value-wins description covers only part of that history.
Compare a fresh literal with two spreads. The ordinary eligible values define data properties in the new object, and the later quantity determines the final stored quantity. That construction does not replay the existing model setter once for each source. Its source getters, if any, remain a separate possible source of effects.
Use distinct source labels in the ledger. Defaults supplies seven, user choice supplies eleven. The target setter trace is defaults-seven followed by user-eleven under the controlled source order. The final stored value can be eleven, but a diagnostic should not claim that defaults was never applied merely because its value is no longer visible.
Change the second source so it lacks quantity. The first quantity remains the relevant copied value in both ordinary comparisons. A missing key does not mean an explicit write of undefined. If the second source contains an enumerable quantity whose value is undefined, that is a real later value and can reach the setter or destination data property.
The independent check reverses source order. Predict both the final value and the setter log before running it. This catches an implementation review that checks only the merged object while ignoring the sequence of model updates. When update effects matter, source order is an operational decision as well as a data precedence rule.
CHAPTER 8 OF 14 · Trace order and failure
8. Distinguish eligible keys from visible-looking fields
These mechanisms ordinarily consider enumerable own source properties. A field supplied only through the source prototype is different from an own field. A non-enumerable own field is also different from an enumerable one. A developer's console presentation is not a complete specification of which properties participate.
Create a source with an enumerable quantity, a non-enumerable internalNote and an inherited category. In the ordinary fixture, quantity participates while the other two do not become copied values through these paths. The omission should be part of the expected result, rather than diagnosed as a failed setter call.
Enumerable symbol-keyed source properties can participate as well. If a model uses a symbol as a private-looking convention, that spelling alone does not make an enumerable own symbol field invisible to value copying. Test the actual key and descriptor rather than equating symbol with omitted.
The destination can still differ once an eligible symbol value is obtained. An ordinary target accessor at that symbol can respond to assignment, whereas a fresh literal spread creates a data property. This extends the same read-versus-write distinction without introducing a new copying rule for a differently printed key.
For an exercise, write the eligible source key list before predicting any target effect. Then annotate each destination descriptor. This separates a property that never entered the copy from a property whose value entered but whose assignment triggered behaviour or failed.
Do not infer a safe data boundary merely from an apparently small eligible list. The selected getters and destination setters may have wider effects through their code. When a plain export record is required, define the permitted fields and value types deliberately instead of trusting every object that happens to expose the right names.
CHAPTER 9 OF 14 · Trace order and failure
9. Preserve partial changes when a later assignment fails
Object.assign is not a transaction that automatically rolls the target back on failure. Give an ordinary target a writable label followed by a non-writable quantity property. Provide an ordinary source whose eligible key order is label then quantity. The label can update before the quantity assignment throws.
The call fails to return normally, but the target need not equal its earlier state. A catch around the call can observe the exception while later code still sees the updated label. Reporting copy failed, so nothing changed would contradict the controlled fixture.
Change the target quantity into a setter that records its input and then throws. Now the setter effect can occur before failure as well. Whether the backing quantity changed depends on the setter's actual code order. Preserve that code-specific trace rather than assuming that every thrown setter has the same partial state.
This matters when a renderer, validator or another reference already holds the target. A partly updated target can be visible even though the copying function never returned a successful result. Replacing a local variable later does not erase effects seen through other references or an external log.
A fresh spread construction changes which object is exposed, but it is not a universal rollback mechanism. A source getter can already have executed before a later source read throws. No completed record may be returned, while earlier external effects remain. New-object construction and effect-free construction are different claims.
For the practical check, save target-before, target-after, setter log and the failure stage. Use literal primitive field snapshots where appropriate rather than a console reference that might display later state. The point is to preserve evidence of the partial operation so the next recovery step is based on what actually happened.
Add a third source containing a counted getter after the source whose target assignment fails. In this controlled sequence, the later source is not processed after that abrupt failure. Its zero read count helps locate the stopping point, while the earlier changed label proves that the whole operation was not undone. These two observations together describe partial progress more accurately than a single success flag.
CHAPTER 10 OF 14 · Apply the data contract
10. Do not mistake a shallow record for an independent model
Both ordinary value-copying paths can retain references to nested objects. If source.details points to an ordinary details object, a copied details value can point to that same object. Creating a new outer literal does not automatically create an independent nested graph.
Use details with a note initially ready. Construct a spread record, then change record.details.note to revised. The source details can now show revised as well because both paths refer to the same nested object in this fixture. The outer object identities differ, but the nested identity is shared.
The same reference question applies when Object.assign updates a model's ordinary details property. A target setter might deliberately transform or copy the supplied reference, but that is behaviour defined by the setter. The general copying call should not be credited with a deep-copy contract that the fixture did not establish.
This distinction is useful when refactoring form state. Replacing assignment with spread may stop a target setter while still sharing nested values with the old model. The rewrite can therefore change one behaviour without providing the independence the author hoped to gain.
If an export boundary requires independent supported data, choose an explicit representation and copying policy. The ArrayBuffer and structured-cloning specialists explain other contracts with their own limits. Do not combine their names into an informal guarantee that any object can be copied safely while preserving every behaviour.
A useful evidence table distinguishes outer identity, nested identity, property descriptor and setter history. These are independently testable questions. A photograph of two equal-looking objects answers none of them conclusively. The learner should be able to explain which relationship was preserved and which behaviour was intentionally removed.
CHAPTER 11 OF 14 · Apply the data contract
11. Apply the distinction to a quantity-entry form
Consider a fictional workshop page where a visitor enters the text seven in a quantity control. The application passes the string "7" to a model setter that deliberately converts an accepted numeric string, checks an allowed range and stores a number. This is a stipulated local model policy, not a claim about native form validation.
Assigning that eligible source field into the model can invoke the setter and produce a stored number seven. Constructing a fresh spread record from the source can instead retain the string. Both may display the character 7, but later arithmetic and validation can receive different value types.
The practical repair is not to rely on invisible setter side effects accidentally. Decide where parsing and validation belong, make the accepted input contract explicit and test the representation consumed by later code. A refactor that changes the destination mechanism must preserve any required conversion through a deliberate operation.
Now change the source value to a rejected quantity, such as a negative count under the fictional policy. If the setter throws, Object.assign can fail at that stage. A plain spread record can still be constructed with that value because data-property creation is not the model's range validator.
Calling the spread version more reliable merely because it stopped throwing would be misleading. It may have bypassed the rule needed to protect the operation. Conversely, a setter's existence does not prove its validation is complete. Inspect the actual policy and independently check the accepted result.
For transfer, use a form field that is a symbolic code rather than a quantity. Its leading zeros may be meaningful, so the quantity conversion is now inappropriate. The lesson is to preserve the required data and behaviour contract, not to send every field through the same setter or convert every numeric-looking string into a number.
CHAPTER 12 OF 14 · Test and choose a boundary
12. Verify the two destination contracts with fresh fixtures
The controlled checks should observe more than final values. For a target-owned setter, record the supplied value, receiver identity and resulting descriptor. For a literal spread, check the absence of a setter call and the presence of an own data property with the expected value.
Add an inherited-setter fixture and inspect both the receiver's own fields and the prototype descriptor. This catches the mistaken assumption that assignment can only invoke a setter found directly on the target. Keep a non-writable inherited-data fixture separate because it represents a different write condition.
Test source selection with an enumerable own string key, a hidden field, an inherited field and an enumerable symbol. Then test source getter execution using a fresh counter for each mechanism. This ensures the destination comparison does not accidentally claim that one path avoids all source behaviour.
Use two ordered sources to check repeated setter calls, including an explicit undefined override. Test a later write failure after an earlier successful change and assert the actual target-after state. A failure test that checks only TypeError misses the most important recovery implication.
Finally test a shared nested object. Assert both outer inequality and nested identity in the new-record case. This is a compact way to distinguish new outer structure from deep independence without involving any browser UI or external storage.
These fixtures exercise ordinary JavaScript objects in a controlled runtime. They do not certify a framework's reactivity model, a platform object's setters or a transaction guarantee. Extend the test only when a real implementation introduces one of those contracts, and record which additional behaviour was actually checked.
Retain the runtime version with the assertions and expected descriptors. A screenshot showing only seven omits the identity, invocation and property-shape evidence that distinguishes these fixtures. The useful result is the complete bounded observation.
CHAPTER 13 OF 14 · Test and choose a boundary
13. Choose an update boundary that matches the application
Return to the stock card. If the application's requirement is to update an existing model through its setter interface, replacing that operation with a new spread record changes the boundary. The caller must either retain the required update operation or deliberately redesign the model and its consumers around plain data.
If the requirement is to construct a plain report without replaying destination setters, data-property construction can be appropriate. The report should then be treated as that representation, with its own validation and consistency rules. It should not be described as a behaviourally equivalent copy of the original model.
A useful refactoring review starts with a before-and-after contract: which identity receives updates, which validations run, which observers or logs depend on those updates and which nested references remain shared. This turns a syntax rewrite into a review of actual consequences.
Change the central case to two independent preview cards. Both need the same source quantity but separate local state. Merely creating two outer records while sharing a mutable details object may violate that requirement. The setter distinction remains relevant, but nested ownership introduces another explicit design decision.
Next change the source to a computed export with a getter. The destination can still be plain while extraction executes producer code. If predictable extraction is required, ask the producer for a deliberate data record rather than trying several copying spellings until the visible output looks acceptable.
The independent learner should now propose the boundary in words before choosing code. Update the existing validated model, construct a plain export or create independent local state are different jobs. Naming the job gives the copying mechanism something concrete to satisfy and makes accidental lost behaviour easier to detect.
CHAPTER 14 OF 14 · Test and choose a boundary
14. Report reads, writes, identities and effects separately
A precise report says that Object.assign read a selected source value and assigned it into a named target, invoking the target's ordinary setter under the stated descriptor. It records the setter's input and effect without pretending that every target behaves identically.
For the spread comparison, say that the object literal constructed a new record with an own data property. If an earlier literal setter was replaced by a later spread definition, report that descriptor change. Do not say the setter was called with no effect when the recorded fixture shows it was never invoked.
When a copy fails, retain changes and effects already completed. A thrown operation and an unchanged target are separate observations. The source may also have executed getters before the failure. A useful recovery plan starts from that actual state rather than from an assumed rollback.
For a successful result, check the properties that matter to future use: value types, descriptor shape, outer identity, nested references and any required validation. Immediate visual similarity can conceal differences in each of these fields. A refactor deserves evidence at the boundary it is intended to preserve.
To practise independently, design a source quantity, a target setter that multiplies by three and a later source that explicitly supplies undefined. Predict the complete setter log and final model state. Then construct a fresh spread record from the same ordinary source values and explain why its descriptor and effects differ.
The transferable habit is to treat obtaining a value and placing a value as distinct operations. Once the destination write is visible, setter calls stop looking like an accidental property of copying. The author can choose an update or construction mechanism whose behaviour matches the website's actual data contract.
Continue through the verified reading route
Property getter effects during structured cloning
Property getter effects during structured cloning. Compare executable source reads with the destination setter effects developed here.
ArrayBuffer transfer and copied bytes
ArrayBuffer transfer and copied bytes. Use a different explicit representation contract when binary ownership or independent storage matters.
Separating a Frozen Outer Object from Immutable Nested Records. After a shallow copy exists, trace which child records remain shared and which objects a freeze actually protects.
Separating toSpliced Copies from splice Mutation in Sparse Array Edits. Compare a copying array edit with splice’s changed receiver and removed-element return, including shared record identities.
Separating Own Property Presence from Inherited Lookup Results. A field can be found through inheritance without belonging to the inspected record; separate that observation from a later write.
Separating Destructuring Defaults from Supplied Null Values. Local defaulted bindings and newly constructed records are different products; trace the undefined trigger and source state.
Continue from assignment into staged property definition
Separate descriptor preparation from target changes in Object.defineProperties. Compare source descriptor reads, the later definition pass and the partial state left when an otherwise valid instruction meets a restricted target.
Frequently asked questions
Does Object.assign copy the source setter itself?
These ordinary paths copy eligible source values. Object.assign writes those values through the target's assignment behaviour. A target setter can run, but that is different from copying the source's accessor descriptor into the target.
Why does object spread stop my model setter from running?
A new literal constructs its own properties rather than assigning into your existing model. Its data-property creation does not replay that model's setter interface. Preserve any required update and validation behaviour deliberately when changing the design.
Can an inherited setter receive an assigned value?
Yes, under the ordinary prototype and descriptor conditions shown here. Inspect the target's prototype relationship as well as its own descriptor. The fixture should also record the receiver and the backing state it changes.
Does spread avoid running source getters?
No general conclusion follows. Both mechanisms can read eligible source getters. The difference developed here concerns destination writes. The separate structured-cloning guide follows source access and serialization effects in more depth.
If Object.assign throws, is the target unchanged?
Not necessarily. Earlier writes or setter effects may already have occurred. Record the failure stage and target-after state. A catch does not establish that the operation rolled back its completed effects.
Is a spread record a deep independent copy?
A new outer record can still share nested object references. Check the identities and representations the destination needs. Neither the copied appearance nor the absence of a destination setter proves complete independence or validation.
Authoritative sources and further reading
ECMAScript language specification: Object.assign. Eligible source values are read and written through target assignment semantics.
ECMAScript language specification: CopyDataProperties. The data-property creation operation used by object spread.
ECMAScript language specification: object initializers. Literal property definitions, order and the spread construction path.
MDN: Object.assign. API documentation and descriptor, setter and shallow-copy boundaries.
Return to the Websites subject guide, The Gold Standard of Education, or the How X Works hub.