Chapter 7: Staying immutable with untrusted code
The chapter introduces the problem of preserving immutability when copy-on-write code interacts with code that may mutate data.
After this chapter you will be able to
- Immutability at code boundaries. Explain why maintaining immutability in your own code with copy-on-write is not enough when you pass data to legacy code or libraries.
- Immutable cart and mutating promotion. Explain why calling the Black Friday promotion conflicts with the cart’s copy-on-write discipline, and state the role assigned to defensive copying.
- Immutable safe zone boundaries. Explain why the Black Friday code is outside the trusted safe zone and how data crossing the boundary in either direction can threaten immutability.
- Limits of copy-on-write protection. Distinguish why the familiar copy-on-write pattern is insufficient for this interaction and state the protective need that motivates defensive copying.
- Defensive copies at boundaries. Explain why trusted code makes a deep copy when data enters from untrusted code and another deep copy when data leaves for untrusted code.
- Defensive copies at both boundaries. Explain why the cart is deep-copied before passing it to black_friday_promotion() and copied again before its modified contents are used by our code.
- Defensive copying at code boundaries. Explain when to deep-copy data leaving for untrusted code and data entering from it, and which copy your code should use.
- Reusable safe function wrapper. Explain why black_friday_promotion() is wrapped in black_friday_promotion_safe(): the wrapper keeps defensive copying in one place so repeated calls are clearer and less likely to omit it.
- Copying at trust boundaries. Apply the section’s defensive-copying rules to the examples: copy data passed to payrollCalc() and its returned checks, but for user updates that only come from untrusted code, copy the incoming data.
- Shared nothing communication boundaries. Explain how web APIs and Erlang processes use copied data to communicate without sharing references.
- Copying discipline tradeoff. Distinguish when the section favors defensive copying versus copy-on-write, and explain the cost behind that choice.
- Immutability safe zone boundaries. Distinguish when to use copy-on-write versus defensive copying, including where each belongs relative to the immutability safe zone.
- Copying rules and costs. Describe the copying steps for each technique and contrast the depth and relative cost of their copies.
- Deep and shallow copy structure. Distinguish a deep copy from a shallow copy by describing what each can share with the original.
- Selective use of deep copies. Justify using deep copies where untrusted code may not follow copy-on-write, rather than using them everywhere.
- Deep and shallow copying. Distinguish deep copying from shallow copying: a deep copy recursively copies arrays and objects at every level, while a shallow-copy approach can share unchanged structure. Explain why the latter is more efficient and why a deep copy can protect an original from untrusted code.
- Complementary immutability disciplines. Distinguish where copy-on-write and defensive copying are used, and explain why both matter when immutable code interacts with existing code.
- Handling existing code safely. Justify which approaches can preserve immutability when new copy-on-write code must interact with existing code that may modify or pass along its data.
- Defensive copying tradeoff. Explain why defensive copying is more powerful but more expensive than copy-on-write, and why using them together is useful.
What it builds on
- The three steps of the copy-on-write discipline (chapter 6) · Assumes familiarity with the copy-on-write discipline for maintaining immutability in your own code.
- Actions, calculations, and data (chapter 3) · The distinction between data and code that acts on data.
- Visualizing shallow copies and structural sharing (chapter 6) · Shallow copies and structural sharing
Section by section
Introduction
The chapter introduces the problem of preserving immutability when copy-on-write code interacts with code that may mutate data.
- Immutability at code boundaries: Explain why maintaining immutability in your own code with copy-on-write is not enough when you pass data to legacy code or libraries.
Why it matters: It explains why immutability needs attention when data crosses into code you don’t trust.
Key terms: immutability, copy-on-write, legacy code, mutable
7.1 Immutability with legacy code
The section introduces a legacy promotion function that mutates an otherwise immutable shopping cart and names defensive copying as the way to interact with it safely.
- Legacy code meaning: Explain that legacy code means existing code that cannot be replaced right now, even if it uses older practices.
Why it matters: This explains why the promotion function must be accommodated rather than rewritten. - Immutable cart and mutating promotion: Explain why calling the Black Friday promotion conflicts with the cart’s copy-on-write discipline, and state the role assigned to defensive copying.
Why it matters: It identifies the boundary where copy-on-write alone is not enough to interact safely with existing code.
Key terms: legacy code, copy-on-write, defensive copying, black_friday_promotion()
7.2 Our copy-on-write code has to interact with untrusted code
This section explains why exchanging data with code that does not follow copy-on-write threatens an immutable safe zone and calls for stronger protection.
- Immutable safe zone boundaries: Explain why the Black Friday code is outside the trusted safe zone and how data crossing the boundary in either direction can threaten immutability.
Why it matters: Recognizing risks in both directions helps you identify where an immutability boundary needs protection. - Limits of copy-on-write protection: Distinguish why the familiar copy-on-write pattern is insufficient for this interaction and state the protective need that motivates defensive copying.
Why it matters: It explains why the existing immutability discipline alone cannot be relied on at this boundary.
Key terms: safe zone, untrusted code, inputs and outputs, immutability, copy-on-write pattern, defensive copying, modifications
7.3 Defensive copying defends the immutable original
This section explains how defensive copying protects immutable data when it crosses the boundary between trusted and untrusted code.
- Defensive copies at boundaries: Explain why trusted code makes a deep copy when data enters from untrusted code and another deep copy when data leaves for untrusted code.
Why it matters: Copying at both boundaries lets your code preserve its immutable data even when it exchanges data with code it cannot trust.
Key terms: defensive copying, deep copy, safe zone, immutable original, untrusted code
7.4 Implementing defensive copies
This section shows how to use defensive copies when calling code that mutates a shopping cart, without exposing the original or trusting the returned data.
- Defensive copies at both boundaries: Explain why the cart is deep-copied before passing it to black_friday_promotion() and copied again before its modified contents are used by our code.
Why it matters: Copying on both sides lets you use the function’s changes without letting its mutations compromise your immutable data.
Key terms: defensive copies, deep copy, immutable discipline, black_friday_promotion()
7.5 The rules of defensive copying
This section gives two deep-copying rules for preserving immutability when data crosses a boundary with untrusted code.
- Defensive copying at code boundaries: Explain when to deep-copy data leaving for untrusted code and data entering from it, and which copy your code should use.
Why it matters: These boundary copies let your code interact with untrusted code without abandoning its immutable discipline. - Flexible copying rule order: Describe why the two rules can apply in either order and when there may be nothing to copy.
Why it matters: You can apply the rules to different calling relationships without assuming data always flows outward first.
Key terms: defensive copying, untrusted code, deep copy, immutable data, copy as data leaves your code, copy as data enters your code
7.6 Wrapping untrusted code
This section shows how to make untrusted code safer to call by putting defensive copying inside a reusable wrapper and copying data at trust boundaries.
- Reusable safe function wrapper: Explain why black_friday_promotion() is wrapped in black_friday_promotion_safe(): the wrapper keeps defensive copying in one place so repeated calls are clearer and less likely to omit it.
Why it matters: Putting the protection in one wrapper makes repeated calls safer and easier to read. - Copying at trust boundaries: Apply the section’s defensive-copying rules to the examples: copy data passed to payrollCalc() and its returned checks, but for user updates that only come from untrusted code, copy the incoming data.
Why it matters: Following the direction of data flow shows where copies are needed without adding an outgoing copy to an incoming-only case.
Key terms: defensive copying, black_friday_promotion_safe(), untrusted code, trusted code, unsafe zone, payrollCalc(), payrollCalcSafe(), defensive copies
7.7 Defensive copying you may be familiar with
The section shows defensive copying at service and process boundaries, explains why multiple copies of user data are acceptable, and contrasts its cost with copy-on-write.
- Shared nothing communication boundaries: Explain how web APIs and Erlang processes use copied data to communicate without sharing references.
Why it matters: You can recognize defensive copying in systems that do not call it by name. - Copies of user facts: Explain why having multiple copies of user data does not create competing representations of the user.
Why it matters: This resolves the concern that only one copy of user data can be the real one. - Copying discipline tradeoff: Distinguish when the section favors defensive copying versus copy-on-write, and explain the cost behind that choice.
Why it matters: The tradeoff explains why protecting boundaries does not require deep-copying everything inside trusted code.
Key terms: defensive copying, shared nothing architecture, references, data, facts about events, copy-on-write, deep copies, shallow copies, safe zone
7.8 Copy-on-write and defensive copying compared
This section compares when and where to use copy-on-write and defensive copying, and how their copying rules and costs differ.
- Immutability safe zone boundaries: Distinguish when to use copy-on-write versus defensive copying, including where each belongs relative to the immutability safe zone.
Why it matters: The boundary tells you which copying discipline applies to a piece of data. - Copying rules and costs: Describe the copying steps for each technique and contrast the depth and relative cost of their copies.
Why it matters: The different rules explain why defensive copying is used at boundaries rather than for ordinary modifications inside the safe zone.
Key terms: copy-on-write, defensive copying, safe zone, untrusted code, shallow copy, deep copy
7.9 Deep copies are more expensive than shallow copies
This section contrasts deep and shallow copies and explains why deep copies are reserved for boundaries with untrusted code.
- Deep and shallow copy structure: Distinguish a deep copy from a shallow copy by describing what each can share with the original.
Why it matters: The difference in sharing explains both the protection and the cost of deep copying. - Selective use of deep copies: Justify using deep copies where untrusted code may not follow copy-on-write, rather than using them everywhere.
Why it matters: It explains why the extra copying is worthwhile at an untrusted boundary but wasteful elsewhere.
Key terms: deep copy, shallow copy, structure, untrusted code, copy-on-write
7.10 Implementing deep copy in JavaScript is difficult
This section explains how deep copying traverses nested data, contrasts it with shallow copying, and cautions against using a teaching-only JavaScript implementation in production.
- Deep and shallow copying: Distinguish deep copying from shallow copying: a deep copy recursively copies arrays and objects at every level, while a shallow-copy approach can share unchanged structure. Explain why the latter is more efficient and why a deep copy can protect an original from untrusted code.
Why it matters: The distinction helps you choose between efficient structural sharing and isolating data from code you do not trust. - JavaScript deep-copy implementation limits: Explain why the section recommends a widely used library’s deep-copy function for production JavaScript rather than its illustrative implementation.
Why it matters: A convincing example of the idea is not necessarily safe for production JavaScript data.
Key terms: deep copying, shallow copying, share structure, untrusted code, standard library, _.cloneDeep(), Lodash, JSON-legal types
7.11 A dialogue between copy-on-write and defensive copying
The section compares copy-on-write and defensive copying, then applies their roles to code that interacts with an immutable safe zone.
- Complementary immutability disciplines: Distinguish where copy-on-write and defensive copying are used, and explain why both matter when immutable code interacts with existing code.
Why it matters: Choosing the discipline by where the data is used lets you preserve immutability without deep-copying every modification. - Handling existing code safely: Justify which approaches can preserve immutability when new copy-on-write code must interact with existing code that may modify or pass along its data.
Why it matters: A safe zone stays safe only if code at its boundary is treated according to what it does, not who owns it.
Key terms: copy-on-write, defensive copying, safe zone, shallow copies, deep copies, untrusted code
7.12 Conclusion
The conclusion contrasts defensive copying’s ability to preserve immutability on its own with its copying cost, then explains why it pairs well with copy-on-write.
- Defensive copying tradeoff: Explain why defensive copying is more powerful but more expensive than copy-on-write, and why using them together is useful.
Why it matters: This tradeoff helps you explain why both approaches have a place in immutable code.
Key terms: defensive copying, copy-on-write, shallow copies
7.13 Summary
7.14 Up next…
Concepts explained in this chapter
- defensive copyA technique that copies data intended to be retained so subsequent modifications cannot alter the retained version.
- deep copyA deep copy recursively duplicates a data structure and all nested objects and arrays so that no structural parts are shared with the original.
- untrusted codeProgram code whose behavior or data-modification effects cannot be assumed to follow the calling system's safety or immutability rules.
- copy-on-writeA data-management technique that creates a copy before modifying data so the original remains unchanged.
- shallow copyA shallow copy is a copy of a data structure's top-level container that retains references to the nested values rather than copying them recursively.
- safe zoneA system region in which code is trusted to preserve the intended data-safety and immutability guarantees.
- immutable collectionData or state whose represented values cannot be modified after creation.
- queueA book-specific JavaScript function that creates an encapsulated queue and returns an operation for submitting work to it.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
