From d2faa0d773ba1814b001f026225b789555ba04fa Mon Sep 17 00:00:00 2001 From: Dmitry Snovalnikov Date: Mon, 12 May 2025 20:26:40 -0500 Subject: [PATCH 1/5] Add 01 class --- legacy_code/01-Sprout_wrap-worksheet.md | 75 ++++++ legacy_code/01-Sprout_wrap.md | 288 ++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 legacy_code/01-Sprout_wrap-worksheet.md create mode 100644 legacy_code/01-Sprout_wrap.md diff --git a/legacy_code/01-Sprout_wrap-worksheet.md b/legacy_code/01-Sprout_wrap-worksheet.md new file mode 100644 index 0000000..cc9cce5 --- /dev/null +++ b/legacy_code/01-Sprout_wrap-worksheet.md @@ -0,0 +1,75 @@ +### Exercise 1: Sprout a Simple Class + +- **Objective:** Implement the Sprout Method on a simple class. +- **Task:** + - Create a `Car` class with a `drive()` method that prints `"The car is driving!"`. + - Use the **Sprout Method** to add new functionality (e.g., adding a `fuel()` method) to the `Car` + class, **without modifying** the original `drive()` method. + - Add a `fuel()` method that prints `"Refueling the car!"`. + - Extend the `Car` class to call both `drive()` and `fuel()` in the new method + `drive_with_fuel()`. + +- **Hint:** + - Focus on isolating the changes to the `drive_with_fuel()` method, while keeping the original + `drive()` method intact. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 2: Implement a Wrap Method + +- **Objective:** Apply the **Wrap Method** to enhance functionality. +- **Task:** + - Create a `Coffee` class with a `cost()` method that prints `"Basic coffee: $5"`. + - Implement a **Wrap Method** where a new class, `MilkDecorator`, wraps the `Coffee` class to + modify its behavior by adding a cost for milk. + - The `MilkDecorator` class should call the original `cost()` method of `Coffee` and then add an + additional cost for milk. + - After that, create a `MilkDecorator` instance and call the `cost()` method to see the new + behavior. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 3: Wrapping Multiple Behaviors + +- **Objective:** Apply the **Wrap Method** to chain multiple decorators. +- **Task:** + - Building on the previous exercise, extend the `MilkDecorator` to create a `SugarDecorator`. + - The `SugarDecorator` should add an additional cost for sugar, while still keeping the + `MilkDecorator` functionality. + - Create a `Coffee` object and wrap it with both `MilkDecorator` and `SugarDecorator`. + - Use the `cost()` method to see the total cost after both decorators are applied. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 4: Python Decorator for Wrap Method + +- **Objective:** Implement a **decorator function** in Python to wrap a method. +- **Task:** + - Create a `Car` class with a `drive()` method that prints `"The car is driving!"`. + - Write a **Python decorator function** that adds functionality to the `drive()` method. + For instance, before calling the `drive()` method, the decorator could print + `"Starting the car..."`. + - Apply the decorator function to the `drive()` method and call it. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 5: Refactor with Decorators + +- **Objective:** Refactor an existing method to use a decorator for extra functionality. +- **Task:** + - Given a simple `User` class with a `login()` method that prints `"User logged in"`, write + a decorator that logs an additional message such as `"Tracking login time..."` whenever the + `login()` method is called. + - Apply the decorator to the `login()` method and test it. + +- **Time Limit:** 5-10 minutes + + diff --git a/legacy_code/01-Sprout_wrap.md b/legacy_code/01-Sprout_wrap.md new file mode 100644 index 0000000..99a0b8b --- /dev/null +++ b/legacy_code/01-Sprout_wrap.md @@ -0,0 +1,288 @@ +--- +title: Working Effectively with Legacy Code +subtitle: Chapter 6 – I Need to Make a Change. What Can I Do? +... + +# Pre-work + +- Book: *Working Effectively with Legacy Code* by Michael Feathers +- Focus: Chapter 6 change techniques +- Optional: + +# Chapters + +::: columns + +:::: column + +| Chapter | Time | +| ---------------------------| -------- | +| Warmup | 00:00:00 | +| Sprout Method | 00:05:00 | +| Sprout Class | 00:15:00 | +| Wrap Method | 00:25:00 | + +:::: + +:::: column + +| Chapter | Time | +| ---------------------------| -------- | +| Wrap Class | 00:35:00 | +| Strategy Comparison | 00:45:00 | +| Summary & Q&A | 00:55:00 | + +:::: + +::: + +# Timetable + +| Activity | Time | +| ----------------------------| ------ | +| Warmup & Goals | 5 min | +| Sprout Method & Class | 20 min | +| Wrap Method & Class | 20 min | +| Compare & Discuss | 5 min | +| Summary & Q&A | 10 min | + +# Warmup + +- What’s your current strategy when you need to change legacy code? +- Ever added a wrapper instead of modifying directly? +- Share in chat: "Sprout" or "Wrap" — which sounds safer to you? + +\note{ +Use this to establish mental models and prepare learners to explore change-based techniques. +} + +# Sprout Method + +- Add new logic in a new method +- Call the new method from existing one +- No edits to original logic + +## Example + +```python +def charge(customer): + return new_charge_logic(customer) + +def new_charge_logic(customer): + # New behavior + return 42 +``` + +# Sprout Class + +- Create a new class for new behavior +- Keeps old logic intact +- Ideal for complex changes needing state + +## Example + +```python +def calculate_invoice(cust): + return InvoiceCalculator(cust).run() + +class InvoiceCalculator: + def __init__(self, customer): + self.customer = customer + def run(self): + # New logic here + pass +``` + +# Wrap Method + +- Create a method that wraps (delegates to) the old method +- Adds logic *before* or *after* the call +- Leaves original untouched + +## Example + +```python +def send_invoice(customer): + log_invoice(customer) + real_send_invoice(customer) + +def real_send_invoice(customer): + print("Sending...") +``` + +# Wrap Method Benefits + +- Low risk: no edits to legacy method +- Adds behavior transparently +- Good when you can’t or don’t want to touch legacy + +# Wrap Class + +- Create a new class that wraps the legacy class +- Delegates calls while injecting new logic +- Helps isolate change when subclassing is risky + +## Example + +```python +class EmailSenderWrapper: + def __init__(self, real_sender): + self.real = real_sender + + def send(self, message): + self.log(message) + return self.real.send(message) + + def log(self, msg): + print("Logging email:", msg) +``` + +# Decorators and the Wrap Method + +- Decorators are a special form of wrapping that allows for dynamic extension. +- In the context of the Wrap Method, decorators provide an elegant solution for layering additional behavior. + +### C++ Decorator Pattern Example + +```cpp +#include +#include + +class Coffee { +public: + virtual void cost() const { std::cout << "Basic coffee: $5\n"; } +}; + +class CoffeeDecorator : public Coffee { +protected: + std::unique_ptr coffee; +public: + CoffeeDecorator(std::unique_ptr c) : coffee(std::move(c)) {} + void cost() const override { + coffee->cost(); + } +}; + +class MilkDecorator : public CoffeeDecorator { +public: + MilkDecorator(std::unique_ptr c) : CoffeeDecorator(std::move(c)) {} + void cost() const override { + CoffeeDecorator::cost(); + std::cout << "Adding milk: $1\n"; + } +}; + +int main() { + std::unique_ptr coffee = std::make_unique(); + MilkDecorator decoratedCoffee(std::move(coffee)); + decoratedCoffee.cost(); +} +``` + +\note{ +This C++ code shows how decorators wrap an object and add additional behavior. The `MilkDecorator` class extends the `Coffee` class without altering its original structure. +} + +# Decorator Pattern in Python + +- **Python**: A more dynamic and concise way to implement decorators. +- Can be used to modify methods or class behavior at runtime using functions or class-based decorators. + +### Python Decorator Example + +```python +class Coffee: + def cost(self): + print("Basic coffee: $5") + +class CoffeeDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + self._coffee.cost() + +class MilkDecorator(CoffeeDecorator): + def cost(self): + self._coffee.cost() + print("Adding milk: $1") + +# Usage +coffee = Coffee() +decorated_coffee = MilkDecorator(coffee) +decorated_coffee.cost() +``` + +\note{ +In Python, decorators can be more flexible. The `MilkDecorator` is a class-based decorator that modifies the behavior of the `cost()` method of the `Coffee` class, just as in C++, but with simpler syntax. +} + +# Comparison & Benefits + +| Feature | C++ | Python | +|-----------------------|---------------------------------------------|------------------------------------------| +| Syntax | Classes, inheritance, virtual methods | Functions, classes, dynamic method calls | +| Flexibility | Requires careful memory management | More dynamic and flexible | +| Usage Scenarios | Performance-sensitive environments | More dynamic, quick prototyping | + +\note{ +Summarize the differences in the implementation of decorators in C++ and Python, emphasizing the trade-offs between the two languages in terms of flexibility and performance. +} + +# Code Walkthrough: C++ + +- Step-by-step breakdown of C++ code: + - **Coffee**: Base class with the `cost()` method. + - **CoffeeDecorator**: A wrapper for extending functionality. + - **MilkDecorator**: Adds the milk cost dynamically. + +\note{ +Take the time to walk through the C++ code. Discuss how decorators encapsulate functionality without modifying the original class and how they can be stacked for layered behaviors. +} + +# Code Walkthrough: Python + +- Step-by-step breakdown of Python code: + - **Coffee**: Basic coffee class. + - **CoffeeDecorator**: The decorator base class. + - **MilkDecorator**: Adds milk to the cost dynamically. + +\note{ +Explain how Python decorators are simpler to implement and why this is a great tool for rapid development and cleaner code. +} + +# Q&A and Wrap-up + +- When would you prefer using decorators over other design patterns (like strategy or composite)? +- What are some potential drawbacks to using decorators excessively? + +\note{ +Encourage the students to reflect on practical uses and drawbacks. Ask them to think about when they might reach for this pattern in their own projects. +} + +# When to Use Each + +| Technique | Use When... | +|------------------|-------------------------------------------| +| Sprout Method | Small change fits in one method | +| Sprout Class | Larger change needs state/context | +| Wrap Method | Add logic before/after a method safely | +| Wrap Class | Insert logic across many methods or state | + +\note{ +You can draw a 2x2: *Sprout vs Wrap* on one axis, *Method vs Class* on the other. +Helps learners visualize choices. +} + +# Summary + +- **Sprout** = add new code that old code calls +- **Wrap** = write code that calls into the old code +- Use *Method* for simple logic, *Class* for complex or stateful logic +- These give you *safe entry points* into legacy code + +# Final Thought + +> "You don't need to clean up the whole kitchen to make a cup of tea. Just clear a spot." +> +> — Inspired by Feathers’ philosophy From 3f753d798b79229fcaf9f56fab51cc508491879a Mon Sep 17 00:00:00 2001 From: Dmitry Snovalnikov <77473414+snovalnikov@users.noreply.github.com> Date: Wed, 14 May 2025 21:19:28 -0500 Subject: [PATCH 2/5] Update legacy_code/01-Sprout_wrap.md Co-authored-by: Yury Bayda --- legacy_code/01-Sprout_wrap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/legacy_code/01-Sprout_wrap.md b/legacy_code/01-Sprout_wrap.md index 99a0b8b..b086f37 100644 --- a/legacy_code/01-Sprout_wrap.md +++ b/legacy_code/01-Sprout_wrap.md @@ -173,7 +173,7 @@ public: }; int main() { - std::unique_ptr coffee = std::make_unique(); + auto coffee = std::make_unique(); MilkDecorator decoratedCoffee(std::move(coffee)); decoratedCoffee.cost(); } From 6e89fe86587ce5690dc9b64e1fb2099af46c3d3d Mon Sep 17 00:00:00 2001 From: Dmitry Snovalnikov <77473414+snovalnikov@users.noreply.github.com> Date: Wed, 14 May 2025 21:19:37 -0500 Subject: [PATCH 3/5] Update legacy_code/01-Sprout_wrap.md Co-authored-by: Yury Bayda --- legacy_code/01-Sprout_wrap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/legacy_code/01-Sprout_wrap.md b/legacy_code/01-Sprout_wrap.md index b086f37..dc942f3 100644 --- a/legacy_code/01-Sprout_wrap.md +++ b/legacy_code/01-Sprout_wrap.md @@ -174,7 +174,7 @@ public: int main() { auto coffee = std::make_unique(); - MilkDecorator decoratedCoffee(std::move(coffee)); + MilkDecorator decorated_coffee(std::move(coffee)); decoratedCoffee.cost(); } ``` From fe3ffe7b3831ec6d01215da57f69a30d187f8d75 Mon Sep 17 00:00:00 2001 From: Dmitry Snovalnikov <77473414+snovalnikov@users.noreply.github.com> Date: Wed, 14 May 2025 21:19:59 -0500 Subject: [PATCH 4/5] Update legacy_code/01-Sprout_wrap.md Co-authored-by: Yury Bayda --- legacy_code/01-Sprout_wrap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/legacy_code/01-Sprout_wrap.md b/legacy_code/01-Sprout_wrap.md index dc942f3..363ad3a 100644 --- a/legacy_code/01-Sprout_wrap.md +++ b/legacy_code/01-Sprout_wrap.md @@ -6,7 +6,7 @@ subtitle: Chapter 6 – I Need to Make a Change. What Can I Do? # Pre-work - Book: *Working Effectively with Legacy Code* by Michael Feathers -- Focus: Chapter 6 change techniques +- Focus: Chapter 6: Change Techniques - Optional: # Chapters From a75dcf9c64d6147da8152d183af03e7f731f7c47 Mon Sep 17 00:00:00 2001 From: Dmitry Snovalnikov Date: Mon, 12 May 2025 20:26:40 -0500 Subject: [PATCH 5/5] Add 01 class --- legacy-code/01-sprout-wrap-worksheet.md | 75 ++++++ legacy-code/01-sprout-wrap.md | 288 ++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 legacy-code/01-sprout-wrap-worksheet.md create mode 100644 legacy-code/01-sprout-wrap.md diff --git a/legacy-code/01-sprout-wrap-worksheet.md b/legacy-code/01-sprout-wrap-worksheet.md new file mode 100644 index 0000000..cc9cce5 --- /dev/null +++ b/legacy-code/01-sprout-wrap-worksheet.md @@ -0,0 +1,75 @@ +### Exercise 1: Sprout a Simple Class + +- **Objective:** Implement the Sprout Method on a simple class. +- **Task:** + - Create a `Car` class with a `drive()` method that prints `"The car is driving!"`. + - Use the **Sprout Method** to add new functionality (e.g., adding a `fuel()` method) to the `Car` + class, **without modifying** the original `drive()` method. + - Add a `fuel()` method that prints `"Refueling the car!"`. + - Extend the `Car` class to call both `drive()` and `fuel()` in the new method + `drive_with_fuel()`. + +- **Hint:** + - Focus on isolating the changes to the `drive_with_fuel()` method, while keeping the original + `drive()` method intact. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 2: Implement a Wrap Method + +- **Objective:** Apply the **Wrap Method** to enhance functionality. +- **Task:** + - Create a `Coffee` class with a `cost()` method that prints `"Basic coffee: $5"`. + - Implement a **Wrap Method** where a new class, `MilkDecorator`, wraps the `Coffee` class to + modify its behavior by adding a cost for milk. + - The `MilkDecorator` class should call the original `cost()` method of `Coffee` and then add an + additional cost for milk. + - After that, create a `MilkDecorator` instance and call the `cost()` method to see the new + behavior. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 3: Wrapping Multiple Behaviors + +- **Objective:** Apply the **Wrap Method** to chain multiple decorators. +- **Task:** + - Building on the previous exercise, extend the `MilkDecorator` to create a `SugarDecorator`. + - The `SugarDecorator` should add an additional cost for sugar, while still keeping the + `MilkDecorator` functionality. + - Create a `Coffee` object and wrap it with both `MilkDecorator` and `SugarDecorator`. + - Use the `cost()` method to see the total cost after both decorators are applied. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 4: Python Decorator for Wrap Method + +- **Objective:** Implement a **decorator function** in Python to wrap a method. +- **Task:** + - Create a `Car` class with a `drive()` method that prints `"The car is driving!"`. + - Write a **Python decorator function** that adds functionality to the `drive()` method. + For instance, before calling the `drive()` method, the decorator could print + `"Starting the car..."`. + - Apply the decorator function to the `drive()` method and call it. + +- **Time Limit:** 5-10 minutes + +--- + +### Exercise 5: Refactor with Decorators + +- **Objective:** Refactor an existing method to use a decorator for extra functionality. +- **Task:** + - Given a simple `User` class with a `login()` method that prints `"User logged in"`, write + a decorator that logs an additional message such as `"Tracking login time..."` whenever the + `login()` method is called. + - Apply the decorator to the `login()` method and test it. + +- **Time Limit:** 5-10 minutes + + diff --git a/legacy-code/01-sprout-wrap.md b/legacy-code/01-sprout-wrap.md new file mode 100644 index 0000000..99a0b8b --- /dev/null +++ b/legacy-code/01-sprout-wrap.md @@ -0,0 +1,288 @@ +--- +title: Working Effectively with Legacy Code +subtitle: Chapter 6 – I Need to Make a Change. What Can I Do? +... + +# Pre-work + +- Book: *Working Effectively with Legacy Code* by Michael Feathers +- Focus: Chapter 6 change techniques +- Optional: + +# Chapters + +::: columns + +:::: column + +| Chapter | Time | +| ---------------------------| -------- | +| Warmup | 00:00:00 | +| Sprout Method | 00:05:00 | +| Sprout Class | 00:15:00 | +| Wrap Method | 00:25:00 | + +:::: + +:::: column + +| Chapter | Time | +| ---------------------------| -------- | +| Wrap Class | 00:35:00 | +| Strategy Comparison | 00:45:00 | +| Summary & Q&A | 00:55:00 | + +:::: + +::: + +# Timetable + +| Activity | Time | +| ----------------------------| ------ | +| Warmup & Goals | 5 min | +| Sprout Method & Class | 20 min | +| Wrap Method & Class | 20 min | +| Compare & Discuss | 5 min | +| Summary & Q&A | 10 min | + +# Warmup + +- What’s your current strategy when you need to change legacy code? +- Ever added a wrapper instead of modifying directly? +- Share in chat: "Sprout" or "Wrap" — which sounds safer to you? + +\note{ +Use this to establish mental models and prepare learners to explore change-based techniques. +} + +# Sprout Method + +- Add new logic in a new method +- Call the new method from existing one +- No edits to original logic + +## Example + +```python +def charge(customer): + return new_charge_logic(customer) + +def new_charge_logic(customer): + # New behavior + return 42 +``` + +# Sprout Class + +- Create a new class for new behavior +- Keeps old logic intact +- Ideal for complex changes needing state + +## Example + +```python +def calculate_invoice(cust): + return InvoiceCalculator(cust).run() + +class InvoiceCalculator: + def __init__(self, customer): + self.customer = customer + def run(self): + # New logic here + pass +``` + +# Wrap Method + +- Create a method that wraps (delegates to) the old method +- Adds logic *before* or *after* the call +- Leaves original untouched + +## Example + +```python +def send_invoice(customer): + log_invoice(customer) + real_send_invoice(customer) + +def real_send_invoice(customer): + print("Sending...") +``` + +# Wrap Method Benefits + +- Low risk: no edits to legacy method +- Adds behavior transparently +- Good when you can’t or don’t want to touch legacy + +# Wrap Class + +- Create a new class that wraps the legacy class +- Delegates calls while injecting new logic +- Helps isolate change when subclassing is risky + +## Example + +```python +class EmailSenderWrapper: + def __init__(self, real_sender): + self.real = real_sender + + def send(self, message): + self.log(message) + return self.real.send(message) + + def log(self, msg): + print("Logging email:", msg) +``` + +# Decorators and the Wrap Method + +- Decorators are a special form of wrapping that allows for dynamic extension. +- In the context of the Wrap Method, decorators provide an elegant solution for layering additional behavior. + +### C++ Decorator Pattern Example + +```cpp +#include +#include + +class Coffee { +public: + virtual void cost() const { std::cout << "Basic coffee: $5\n"; } +}; + +class CoffeeDecorator : public Coffee { +protected: + std::unique_ptr coffee; +public: + CoffeeDecorator(std::unique_ptr c) : coffee(std::move(c)) {} + void cost() const override { + coffee->cost(); + } +}; + +class MilkDecorator : public CoffeeDecorator { +public: + MilkDecorator(std::unique_ptr c) : CoffeeDecorator(std::move(c)) {} + void cost() const override { + CoffeeDecorator::cost(); + std::cout << "Adding milk: $1\n"; + } +}; + +int main() { + std::unique_ptr coffee = std::make_unique(); + MilkDecorator decoratedCoffee(std::move(coffee)); + decoratedCoffee.cost(); +} +``` + +\note{ +This C++ code shows how decorators wrap an object and add additional behavior. The `MilkDecorator` class extends the `Coffee` class without altering its original structure. +} + +# Decorator Pattern in Python + +- **Python**: A more dynamic and concise way to implement decorators. +- Can be used to modify methods or class behavior at runtime using functions or class-based decorators. + +### Python Decorator Example + +```python +class Coffee: + def cost(self): + print("Basic coffee: $5") + +class CoffeeDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + self._coffee.cost() + +class MilkDecorator(CoffeeDecorator): + def cost(self): + self._coffee.cost() + print("Adding milk: $1") + +# Usage +coffee = Coffee() +decorated_coffee = MilkDecorator(coffee) +decorated_coffee.cost() +``` + +\note{ +In Python, decorators can be more flexible. The `MilkDecorator` is a class-based decorator that modifies the behavior of the `cost()` method of the `Coffee` class, just as in C++, but with simpler syntax. +} + +# Comparison & Benefits + +| Feature | C++ | Python | +|-----------------------|---------------------------------------------|------------------------------------------| +| Syntax | Classes, inheritance, virtual methods | Functions, classes, dynamic method calls | +| Flexibility | Requires careful memory management | More dynamic and flexible | +| Usage Scenarios | Performance-sensitive environments | More dynamic, quick prototyping | + +\note{ +Summarize the differences in the implementation of decorators in C++ and Python, emphasizing the trade-offs between the two languages in terms of flexibility and performance. +} + +# Code Walkthrough: C++ + +- Step-by-step breakdown of C++ code: + - **Coffee**: Base class with the `cost()` method. + - **CoffeeDecorator**: A wrapper for extending functionality. + - **MilkDecorator**: Adds the milk cost dynamically. + +\note{ +Take the time to walk through the C++ code. Discuss how decorators encapsulate functionality without modifying the original class and how they can be stacked for layered behaviors. +} + +# Code Walkthrough: Python + +- Step-by-step breakdown of Python code: + - **Coffee**: Basic coffee class. + - **CoffeeDecorator**: The decorator base class. + - **MilkDecorator**: Adds milk to the cost dynamically. + +\note{ +Explain how Python decorators are simpler to implement and why this is a great tool for rapid development and cleaner code. +} + +# Q&A and Wrap-up + +- When would you prefer using decorators over other design patterns (like strategy or composite)? +- What are some potential drawbacks to using decorators excessively? + +\note{ +Encourage the students to reflect on practical uses and drawbacks. Ask them to think about when they might reach for this pattern in their own projects. +} + +# When to Use Each + +| Technique | Use When... | +|------------------|-------------------------------------------| +| Sprout Method | Small change fits in one method | +| Sprout Class | Larger change needs state/context | +| Wrap Method | Add logic before/after a method safely | +| Wrap Class | Insert logic across many methods or state | + +\note{ +You can draw a 2x2: *Sprout vs Wrap* on one axis, *Method vs Class* on the other. +Helps learners visualize choices. +} + +# Summary + +- **Sprout** = add new code that old code calls +- **Wrap** = write code that calls into the old code +- Use *Method* for simple logic, *Class* for complex or stateful logic +- These give you *safe entry points* into legacy code + +# Final Thought + +> "You don't need to clean up the whole kitchen to make a cup of tea. Just clear a spot." +> +> — Inspired by Feathers’ philosophy