diff --git a/notes/unit1_code_quality/principles.md b/notes/unit1_code_quality/principles.md
index a3bb9b0..5e4e6b2 100644
--- a/notes/unit1_code_quality/principles.md
+++ b/notes/unit1_code_quality/principles.md
@@ -1227,6 +1227,9 @@ software that is well-structured, scalable, and maintainable. They help in manag
complexity, improving collaboration, and ensuring that the software can adapt to change
while remaining reliable and efficient over time.
+To find out about other common principles, please visit the
+[DevIQ](https://deviq.com/principles/principles-overview) website.
+
{: .tip-title }
> [ General tips for applying software engineering principles](principles_guidelines.md)
diff --git a/notes/unit1_code_quality/refactoring_techniques.md b/notes/unit1_code_quality/refactoring_techniques.md
index e69de29..1e0f145 100644
--- a/notes/unit1_code_quality/refactoring_techniques.md
+++ b/notes/unit1_code_quality/refactoring_techniques.md
@@ -0,0 +1,889 @@
+# Refactoring techniques
+
+Refactoring is the process of improving the internal structure of existing code without altering
+its external behavior. It involves making incremental changes to the codebase to enhance
+readability, maintainability, and performance while ensuring that the system continues to
+function as expected. Refactoring is a critical practice in software engineering because,
+over time, codebases can become cluttered, difficult to manage, or suffer from technical debt
+due to rapid development, evolving requirements, or poor initial design.
+
+The primary goal of refactoring is to make the code cleaner, more efficient, and easier to
+extend while maintaining its original functionality. This often involves simplifying complex
+logic, removing duplication, improving the organization of classes and methods, and applying
+design patterns where appropriate. Effective refactoring helps prevent bugs, reduces the cost
+of future modifications, and enhances overall code quality, leading to more robust and scalable
+software systems.
+
+In these notes, we will explore various refactoring techniques, from simple adjustments like
+renaming variables and extracting methods, to more advanced techniques like restructuring large
+classes or applying design patterns. Understanding and applying these techniques is essential
+for software engineers who aim to write high-quality, maintainable code that can adapt to change
+over time. To use them effectively, you need to understand basic concepts from object orientation
+such as encapsulation, inheritance and polymorphism, and you need a good working knowledge
+of software engineering principles and design patterns.
+
+## Simple Refactoring Techniques
+
+Some refactoring techniques are just about writing readable and efficient code. You may need to
+apply some of the following, for example, if you realise that you have inadvertently violated
+the KISS or DRY principles.
+
+1. **Rename Variables or Methods**
+
+ Renaming variables or methods to more meaningful names improves code readability and
+ helps convey the intent of the code.
+
+
+
+ Before:
+
+ ``` c#
+ public class Order
+ {
+ public double x; // "x" isn't clear
+
+ public void Calc() // Method name isn't descriptive
+ {
+ x = x * 1.1;
+ }
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public class Order
+ {
+ public double totalAmount; // Clear, descriptive variable name
+
+ public void CalculateTotalWithTax() // Method name conveys intent
+ {
+ totalAmount = totalAmount * 1.1;
+ }
+ }
+ ```
+
+ The code is more readable and self-explanatory. This reduces the need for comments to
+ explain what `x` or `Calc()` do.
+
+
+
+2. **Extract Method**
+
+ When you have a block of code that does a specific task, extract it into a separate method.
+ This simplifies the original method and promotes code reuse.
+
+
+
+ Before:
+
+ ``` c#
+ public void ProcessOrder(Order order)
+ {
+ // Calculate discount
+ if (order.Amount > 100)
+ {
+ order.Amount -= order.Amount * 0.1;
+ }
+
+ // Print receipt
+ Console.WriteLine($"Order total: {order.Amount}");
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public void ProcessOrder(Order order)
+ {
+ ApplyDiscount(order); // Extracted method
+ PrintReceipt(order); // Extracted method
+ }
+
+ private void ApplyDiscount(Order order)
+ {
+ if (order.Amount > 100)
+ {
+ order.Amount -= order.Amount * 0.1;
+ }
+ }
+
+ private void PrintReceipt(Order order)
+ {
+ Console.WriteLine($"Order total: {order.Amount}");
+ }
+ ```
+
+ The `ProcessOrder` method is now shorter, easier to read, and each extracted method has a
+ clear responsibility.
+
+
+
+3. **Inline Method**
+
+ If a method’s body is simple and is only used in one place, you can replace the method
+ call with the method body to reduce unnecessary indirection.
+
+
+
+ Before:
+
+ ``` c#
+ public void ProcessOrder(Order order)
+ {
+ double total = GetTotal(order);
+ Console.WriteLine($"Total: {total}");
+ }
+
+ private double GetTotal(Order order)
+ {
+ return order.Amount;
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public void ProcessOrder(Order order)
+ {
+ double total = order.Amount; // Inlined method
+ Console.WriteLine($"Total: {total}");
+ }
+ ```
+
+ The `GetTotal` method was redundant. Inlining simplifies the code and removes
+ unnecessary abstraction.
+
+
+
+4. **Replace Magic Numbers with Constants**
+
+ Using "magic numbers" (hard-coded numeric values) makes code less readable. Replace them
+ with constants that describe their meaning.
+
+
+
+ Before:
+
+ ``` c#
+ public void ApplyDiscount(Order order)
+ {
+ order.Amount -= order.Amount * 0.05; // What does 0.05 represent?
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public const double DiscountRate = 0.05;
+
+ public void ApplyDiscount(Order order)
+ {
+ order.Amount -= order.Amount * DiscountRate; // Clear intent
+ }
+ ```
+
+ The use of constants makes the code easier to understand, and future changes to the
+ discount rate can be made in one place.
+
+
+
+5. **Introduce Explaining Variable**
+
+ Use variables to clarify the meaning of complex expressions by breaking them down into
+ smaller, more understandable parts.
+
+
+
+ Before:
+
+ ``` c#
+ public bool IsEligibleForDiscount(Order order)
+ {
+ return order.Amount > 100 && order.Customer.HasLoyaltyPoints && order.Items.Count > 5;
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public bool IsEligibleForDiscount(Order order)
+ {
+ bool hasEnoughAmount = order.Amount > 100;
+ bool hasLoyaltyPoints = order.Customer.HasLoyaltyPoints;
+ bool hasEnoughItems = order.Items.Count > 5;
+
+ return hasEnoughAmount && hasLoyaltyPoints && hasEnoughItems;
+ }
+ ```
+
+ Breaking down the expression into smaller variables makes the code easier to read and
+ understand at a glance.
+
+
+
+6. **Consolidate Duplicate Conditional Fragments**
+
+ If the same code appears in multiple branches of a conditional, move it outside the
+ conditional to eliminate duplication.
+
+
+
+ Before:
+
+ ``` c#
+ public void PrintReceipt(Order order)
+ {
+ if (order.IsPriority)
+ {
+ Console.WriteLine("Priority Order");
+ Console.WriteLine($"Total: {order.Amount}");
+ }
+ else
+ {
+ Console.WriteLine("Standard Order");
+ Console.WriteLine($"Total: {order.Amount}");
+ }
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public void PrintReceipt(Order order)
+ {
+ if (order.IsPriority)
+ {
+ Console.WriteLine("Priority Order");
+ }
+ else
+ {
+ Console.WriteLine("Standard Order");
+ }
+
+ Console.WriteLine($"Total: {order.Amount}"); // Duplicated code moved outside the conditional
+ }
+ ```
+
+ Duplicated code is reduced, making the code more concise and easier to maintain.
+
+
+
+7. **Encapsulate Field**
+
+ Directly accessing fields breaks encapsulation. Use getter and setter methods to control
+ access to a field, allowing better control over how the field is accessed or modified.
+
+
+
+ Before:
+
+ ``` c#
+ public class Order
+ {
+ public double amount; // Direct field access
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public class Order
+ {
+ private double amount;
+
+ public double GetAmount() // Encapsulated field access
+ {
+ return amount;
+ }
+
+ public void SetAmount(double value)
+ {
+ amount = value;
+ }
+ }
+ ```
+
+ Encapsulation provides flexibility to add validation, logging, or other logic when
+ getting or setting values, without exposing the internal implementation.
+
+
+
+8. **Decompose Conditional**
+
+ Complex conditionals can be hard to understand. Break them into separate methods or
+ use guard clauses to improve clarity.
+
+
+
+ Before:
+
+ ``` c#
+ public double CalculateShippingCost(Order order)
+ {
+ if (order.Customer.IsPremium && order.Amount > 100)
+ {
+ return 0;
+ }
+ else if (order.Customer.IsPremium)
+ {
+ return 5;
+ }
+ else
+ {
+ return 10;
+ }
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public double CalculateShippingCost(Order order)
+ {
+ if (IsFreeShipping(order)) return 0;
+ if (IsDiscountedShipping(order)) return 5;
+ return 10;
+ }
+
+ private bool IsFreeShipping(Order order)
+ {
+ return order.Customer.IsPremium && order.Amount > 100;
+ }
+
+ private bool IsDiscountedShipping(Order order)
+ {
+ return order.Customer.IsPremium;
+ }
+ ```
+
+ The conditional logic is now more readable and easier to modify or extend.
+
+
+
+## Advanced refactoring techniques
+
+Some problems run a little deeper than just improving code readability. Identifying and
+resolving structural issues with your core requires a good working knowledge of software
+engineering principles and design patterns.
+
+1. **Extract Class**
+
+ When a class becomes too large and takes on too many responsibilities, it violates the
+ Single Responsibility Principle (SRP). To correct this, you can refactor by splitting
+ the class into smaller, more focused classes.
+
+
+
+ Before:
+
+ ``` c#
+ public class Customer
+ {
+ public string Name { get; set; }
+ public string Email { get; set; }
+
+ // Address information
+ public string Street { get; set; }
+ public string City { get; set; }
+ public string ZipCode { get; set; }
+
+ public void SendEmail()
+ {
+ Console.WriteLine($"Sending email to {Email}");
+ }
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public class Customer
+ {
+ public string Name { get; set; }
+ public string Email { get; set; }
+ public Address Address { get; set; } // Address extracted into its own class
+
+ public void SendEmail()
+ {
+ Console.WriteLine($"Sending email to {Email}");
+ }
+ }
+
+ public class Address
+ {
+ public string Street { get; set; }
+ public string City { get; set; }
+ public string ZipCode { get; set; }
+ }
+ ```
+
+ This refactor follows the Single Responsibility Principle (SRP), as each class is now
+ responsible for one thing. The `Customer` class handles customer-specific data and behavior,
+ while the `Address` class encapsulates address-related information. This separation
+ simplifies future modifications, improves code readability, and reduces the likelihood
+ of changes to one class affecting the other.
+
+
+
+2. **Move Method**
+
+ If a method in one class uses data from another class more than its own, it may be better
+ suited in the other class. This promotes cohesion by ensuring related functionality
+ resides together.
+
+
+
+ Before:
+
+ ``` c#
+ public class Order
+ {
+ public double Amount { get; set; }
+ public Customer Customer { get; set; }
+
+ public double CalculateDiscount()
+ {
+ if (Customer.IsLoyalCustomer)
+ {
+ return Amount * 0.1;
+ }
+ return 0;
+ }
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public class Order
+ {
+ public double Amount { get; set; }
+ public Customer Customer { get; set; }
+
+ public double ApplyDiscount()
+ {
+ return Customer.CalculateDiscount(Amount); // Move discount logic to Customer
+ }
+ }
+
+ public class Customer
+ {
+ public bool IsLoyalCustomer { get; set; }
+
+ public double CalculateDiscount(double amount)
+ {
+ return IsLoyalCustomer ? amount * 0.1 : 0;
+ }
+ }
+ ```
+
+ This refactor adheres to the [Law of Demeter](https://deviq.com/laws/law-of-demeter)
+ (also known as the principle of least knowledge) and SRP. The discount logic is more
+ naturally tied to the Customer class, as it depends on customer properties. Moving this
+ method ensures that classes deal primarily with their own data, improving cohesion and
+ making the code easier to understand and maintain.
+
+
+
+3. **Replace Conditional with Polymorphism**
+
+ When you have conditionals (e.g., if-else or switch statements) that change behavior
+ based on the type of an object, you can refactor by using polymorphism to simplify the
+ code. This adheres to the Open/Closed Principle (OCP), as the class can now be extended
+ without modification.
+
+
+
+ Before:
+
+ ``` c#
+ public class Employee
+ {
+ public string Type { get; set; }
+
+ public double CalculateBonus(double salary)
+ {
+ if (Type == "Manager")
+ {
+ return salary * 0.5;
+ }
+ else if (Type == "Developer")
+ {
+ return salary * 0.2;
+ }
+ return 0;
+ }
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public abstract class Employee
+ {
+ public abstract double CalculateBonus(double salary);
+ }
+
+ public class Manager : Employee
+ {
+ public override double CalculateBonus(double salary)
+ {
+ return salary * 0.5;
+ }
+ }
+
+ public class Developer : Employee
+ {
+ public override double CalculateBonus(double salary)
+ {
+ return salary * 0.2;
+ }
+ }
+ ```
+
+ By replacing the conditional logic with polymorphism, we follow the Open/Closed Principle
+ (OCP). Now, the `Employee` class can be extended by adding new types (e.g., `Intern`,
+ `Consultant`) without modifying the existing code, thus reducing the risk of breaking
+ functionality when the system evolves. It also improves code readability and removes
+ clutter from conditionals.
+
+
+
+4. **Replace Data Value with Object**
+
+ When primitive data is used to represent multiple attributes or behaviors, you can
+ refactor it into a class that represents that concept. This follows Encapsulation and SRP.
+
+
+
+ Before:
+
+ ``` c#
+ public class Order
+ {
+ public string ProductName { get; set; }
+ public int Quantity { get; set; }
+ public double Price { get; set; }
+
+ public double GetTotalPrice()
+ {
+ return Quantity * Price;
+ }
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public class Product
+ {
+ public string Name { get; set; }
+ public double Price { get; set; }
+ }
+
+ public class Order
+ {
+ public Product Product { get; set; }
+ public int Quantity { get; set; }
+
+ public double GetTotalPrice()
+ {
+ return Quantity * Product.Price;
+ }
+ }
+ ```
+
+ This refactor improves encapsulation by bundling product-related attributes and behaviors
+ within the `Product` class, keeping the logic grouped logically. It follows SRP, as `Product`
+ now manages its own state, and the `Order` class focuses on order-specific behavior. This
+ separation of concerns makes both classes easier to maintain and extend.
+
+
+
+5. **Introduce Parameter Object**
+
+ When a method has too many parameters, you can refactor by introducing an object that
+ encapsulates related data. This promotes readability and reduces cognitive complexity.
+
+
+
+ Before:
+
+ ``` c#
+ public void CreateOrder(string productName, int quantity, double price, string customerName, string customerAddress)
+ {
+ // Order creation logic
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public class Product
+ {
+ public string Name { get; set; }
+ public double Price { get; set; }
+ }
+
+ public class Customer
+ {
+ public string Name { get; set; }
+ public string Address { get; set; }
+ }
+
+ public void CreateOrder(Product product, int quantity, Customer customer)
+ {
+ // Order creation logic
+ }
+ ```
+
+ This refactor improves code readability by reducing the number of parameters and grouping
+ related data into cohesive objects (`Product` and `Customer`). This follows SRP and reduces
+ the complexity of method signatures, making the code more maintainable and scalable. As the
+ system grows, it is easier to add new properties to `Product` or `Customer` without
+ modifying the method signature.
+
+
+
+6. **Introduce Null Object**
+
+Instead of returning `null` and having many `null` checks in the code, you can introduce a
+Null Object that represents a default behavior or empty state. This improves encapsulation
+and reduces error-prone null checks.
+
+
+
+Before:
+
+``` c#
+public class Customer
+{
+ public Address Address { get; set; }
+}
+
+public string GetCustomerStreet(Customer customer)
+{
+ if (customer == null || customer.Address == null)
+ {
+ return "No Address";
+ }
+ return customer.Address.Street;
+}
+```
+
+After:
+
+``` c#
+public class NullAddress : Address
+{
+ public override string Street => "No Address";
+}
+
+public class Customer
+{
+ public Address Address { get; set; } = new NullAddress(); // Default to Null Object
+}
+
+public string GetCustomerStreet(Customer customer)
+{
+ return customer.Address.Street;
+}
+```
+
+Using a Null Object avoids error-prone `null` checks and simplifies the code. This follows
+the [Tell, Don’t Ask](https://deviq.com/principles/tell-dont-ask) principle, allowing
+methods to ask objects to do things rather than query their state and act accordingly.
+This also enhances encapsulation by allowing objects to manage their own behavior for
+edge cases (like `null` states).
+
+
+
+7. **Replace Temp with Query**
+
+ When a temporary variable is assigned a value that could be calculated directly in the
+ method, consider eliminating the variable and replacing it with a query (method call).
+ This improves readability and adheres to KISS (Keep It Simple, Stupid).
+
+
+
+ Before:
+
+ ``` c#
+ public double CalculateTotal(Order order)
+ {
+ double discount = GetDiscount(order); // Temporary variable
+ return order.Amount - discount;
+ }
+ ```
+
+ After:
+
+ ``` c#
+ public double CalculateTotal(Order order)
+ {
+ return order.Amount - GetDiscount(order); // Replace temp with query
+ }
+ ```
+
+ This refactor follows the KISS principle, as it simplifies the method by removing
+ unnecessary temporary variables. It makes the code easier to read and maintain by
+ eliminating an intermediate step, directly executing the query instead. This also
+ leads to cleaner, more focused methods.
+
+
+
+8. **Encapsulate Collection**
+
+If a class exposes a collection (such as a list) directly, clients of the class can
+modify the collection, breaking encapsulation. Encapsulating the collection ensures
+better control over how the collection is accessed or modified.
+
+
+
+Before:
+
+``` c#
+public class Order
+{
+ public List- Items { get; set; } = new List
- ();
+}
+```
+
+After:
+
+``` c#
+public class Order
+{
+ private List
- items = new List
- ();
+
+ public IReadOnlyList
- Items => items.AsReadOnly(); // Read-only access
+
+ public void AddItem(Item item)
+ {
+ items.Add(item);
+ }
+
+ public void RemoveItem(Item item)
+ {
+ items.Remove(item);
+ }
+}
+```
+
+By encapsulating the collection, you follow encapsulation and information hiding
+principles. The client cannot directly modify the `Items` collection, ensuring that the
+class controls how the collection is manipulated. This protects the internal state and
+enforces consistency in how the collection is accessed.
+
+
+
+## When to apply refactoring
+
+Refactoring is an essential part of the software development process and can be applied at
+various stages to improve the code's structure, readability, maintainability, and performance.
+Here are key points during the development process where refactoring techniques should be applied:
+
+1. During Code Reviews
+
+ During code reviews, team members may identify code smells, duplication, or excessive
+ complexity. This is an ideal time to refactor because it ensures that only clean,
+ maintainable code is merged into the main codebase. Code reviews provide an opportunity
+ to catch potential issues before they become embedded in the project, and refactoring at
+ this stage can prevent future technical debt.
+
+2. After Writing the First Draft of Code
+
+ After completing the initial implementation, it's a good idea to review the code for
+ clarity, structure, and adherence to best practices. The first version of the code often
+ focuses on functionality, and as developers gain a deeper understanding of the problem,
+ it’s important to refactor the code to make it simpler, more modular, and easier to
+ maintain. This step ensures that the initial logic is refined and optimized before it
+ becomes part of the larger codebase.
+
+3. Before Adding New Features
+
+ When preparing to implement new functionality, it’s essential to assess the current
+ state of the code. If the existing codebase is burdened with technical debt or complexity,
+ refactoring it before introducing new features makes the process smoother and reduces
+ the risk of introducing bugs. By cleaning up the code first, developers ensure that the
+ foundation is solid, allowing for easier extension and minimizing future headaches.
+
+4. When Fixing Bugs
+
+ If you encounter confusing or overly complex code while fixing a bug, it is a good practice
+ to refactor that code. Often, poorly structured code can hide bugs or make them harder to
+ resolve. By simplifying and clarifying the code during the bug-fixing process, you not only
+ solve the immediate problem but also reduce the likelihood of encountering similar issues
+ in the future. Refactoring in this context is key to improving the long-term maintainability
+ of the system.
+
+5. When Implementing Code that Violates SOLID Principles
+
+ The SOLID principles are fundamental to writing clean, scalable code. If you notice
+ violations of these principles, refactoring the affected areas ensures that the code
+ adheres to good design practices, making it easier to modify, extend, and maintain as
+ the project grows. This proactive approach prevents the accumulation of technical debt.
+
+6. As Part of Continuous Refactoring (Technical Debt Reduction)
+
+ Incorporating continuous refactoring as part of your daily workflow is another important
+ strategy. Over time, even well-written code can accumulate technical debt as new features
+ are added or requirements change. Regular refactoring, such as setting aside time during
+ each sprint or development cycle, ensures that the code remains clean and manageable.
+ This approach minimizes long-term technical debt and keeps the codebase in a healthy
+ state, allowing the team to focus on adding value rather than constantly fixing problems.
+
+7. When Performance Becomes a Concern
+
+ If you identify performance bottlenecks or inefficient code, refactoring can help you
+ streamline operations, optimize algorithms, or reduce redundancy, all without altering
+ the external behavior of the system. By addressing performance issues through refactoring,
+ you ensure that the code remains both functional and efficient.
+
+8. When the Code Becomes Hard to Understand (Code Smells)
+
+ Over time, complex logic, unclear naming, and code duplication can make it difficult for
+ developers to understand and maintain the code. If you find yourself or others struggling
+ to grasp the code’s purpose or flow, refactoring can help. This might involve renaming
+ variables, breaking down large methods into smaller, more manageable ones, or simplifying
+ logic to make the code more readable. This approach ensures that the code remains accessible
+ and maintainable, even as the project grows.
+
+9. Before Merging Code into the Main Branch
+
+ Ensuring that the newly developed feature adheres to coding standards, doesn’t introduce
+ unnecessary complexity, and follows best practices is critical. Refactoring before merging
+ ensures that the codebase remains clean, scalable, and free of technical debt, which is
+ especially important for long-term maintenance.
+
+10. When Onboarding New Team Members
+
+ If new developers struggle to understand the existing codebase, it may be a sign that
+ the code needs refactoring for clarity. Refactoring at this stage not only helps the new
+ team members become productive faster but also improves the overall quality of the code,
+ making it easier for everyone to work with in the long term.
+
+11. During Legacy Code Maintenance
+
+ When working on legacy systems, you often encounter outdated practices, overly complex
+ logic, or code that violates modern principles. Refactoring legacy code ensures that it
+ adheres to current standards and becomes easier to maintain and extend. This approach helps
+ breathe new life into old systems and allows them to evolve with the project’s needs.
+
+## Conclusion
+
+Refactoring is a key practice for improving code quality, readability, and maintainability.
+Simple refactoring techniques can be applied to most codebases and will lead to more
+robust and clean code. More advanced refactoring techniques align with key software engineering
+principles like Single Responsibility (SRP), Open/Closed Principle (OCP), and Encapsulation.
+
+Refactoring should be applied continuously throughout the development process. By refactoring
+early and often, you can improve code quality, reduce technical debt, and keep the codebase
+adaptable for future growth. Applying refactoring techniques at key points—such as during
+code reviews, after bug fixes, or before adding new features—ensures that the code remains
+clean, readable, and maintainable over time.
+
+{: .tip-title }
+> [ Tips for refactoring](refactoring_techniques_guidelines.md)
+
diff --git a/notes/unit1_code_quality/refactoring_techniques_guidelines.md b/notes/unit1_code_quality/refactoring_techniques_guidelines.md
new file mode 100644
index 0000000..79cf93f
--- /dev/null
+++ b/notes/unit1_code_quality/refactoring_techniques_guidelines.md
@@ -0,0 +1,136 @@
+# Tips for improving your refactoring practice
+
+By incorporating these tips into your daily workflow, you’ll not only develop a habit of
+refactoring but also gain the skills to recognize when and where refactoring is needed.
+These practices will help ensure that your code remains clean, maintainable, and adaptable
+over time.
+
+1. **Learn to Spot Code Smells**
+
+ > Familiarize yourself with common code smells — indicators that something might be wrong
+ > with your code. Examples include long methods, large classes, duplicate code, overly
+ > complex conditional logic, and many parameters in a method.
+ >
+ > Whenever you encounter these issues, consider refactoring as a solution to simplify and
+ > improve the code structure.
+
+2. **Refactor Small, Frequent Changes**
+
+ > Refactoring doesn’t always mean large-scale changes. Get in the habit of making small,
+ > incremental improvements whenever you touch code.
+ >
+ > Each time you work on a feature or fix a bug, look for small opportunities to refactor,
+ > such as renaming variables, extracting methods, or reducing duplication.
+
+3. **Apply the Boy Scout Rule**
+
+ > Leave the code cleaner than you found it. Even if you didn’t write the code originally,
+ > improve it when you come across something unclear or messy.
+ >
+ > When working on a feature or bug, take a moment to refactor parts of the codebase that
+ > are confusing or poorly structured, without changing its functionality.
+
+4. **Avoid Over-Engineering**
+
+ > Refactoring is about simplifying and improving existing code, not making it more complex.
+ > Don’t over-engineer your solutions in an attempt to make the code "perfect."
+ >
+ > Focus on clarity and simplicity. Use the KISS principle (Keep It Simple, Stupid) to guide
+ > your refactoring efforts. Always ask yourself, "Can I make this code easier to understand?"
+
+5. **Refactor When Adding New Features**
+
+> Before adding a new feature, examine the existing code to see if it’s in good condition.
+> If the code is difficult to extend or modify, refactor it first to make your work easier.
+>
+> Identify areas where you can apply refactoring techniques like Extract Class or Move Method
+> before implementing new functionality. This will prevent new code from being built on a
+> fragile foundation.
+
+6. **Practice Continuous Refactoring**
+
+> Refactoring should be a continuous practice, not a one-off activity. Integrate it into your
+> daily development routine.
+>
+> Set aside time in your workflow to review the code you’ve written or touched for possible
+> improvements. Use refactoring as a daily habit rather than waiting for a major overhaul.
+
+7. **Use Automated Tools**
+
+ > Leverage IDE features or tools (such as [ReSharper](https://www.jetbrains.com/resharper/),
+ > [SonarLint](https://www.sonarsource.com/products/sonarlint/), or
+ > [CodeMaid](https://marketplace.visualstudio.com/items?itemName=SteveCadwallader.CodeMaid))
+ > that can help identify areas for refactoring and suggest improvements.
+ >
+ > Run static analysis tools regularly to check for code smells, duplication, and
+ > inefficiencies, and use them as a guide to refactor consistently.
+
+8. **Use Code Reviews as Learning Opportunities**
+
+ > Participate actively in code reviews, both as a reviewer and a contributor. Reviews can
+ > highlight areas where refactoring can improve code quality.
+ >
+ > When receiving feedback, look for suggestions related to code readability, duplication, or
+ > complexity, and use these opportunities to refactor your code. As a reviewer, point out
+ > refactoring opportunities when reviewing others' code.
+
+9. **Follow the DRY Principle**
+
+ > Follow the DRY (Don’t Repeat Yourself) principle. If you find yourself writing the same
+ > or very similar code in multiple places, it’s a sign you need to refactor.
+ >
+ > Refactor by extracting common logic into reusable methods or classes to avoid duplication
+ > and make the code easier to maintain.
+
+10. **Refactor After Passing Tests**
+
+ > Refactor only after your code is functionally correct and all tests are passing. This
+ > ensures that your refactoring does not introduce new bugs.
+ >
+ > Write or run tests for your code before refactoring. Once everything works as expected,
+ > refactor to improve the code without changing its functionality. This ensures that your
+ > refactoring maintains the intended behavior.
+
+11. **Pay Attention to Method Size**
+
+ > Methods that are too long or do multiple things often need refactoring. Shorter, focused
+ > methods are easier to test, understand, and maintain.
+ >
+ > Break down long methods into smaller, more focused ones, each with a single responsibility.
+ > This will improve code clarity and make your code easier to debug and extend.
+
+12. **Use Design Patterns Thoughtfully**
+
+ > Refactor complex conditional logic or repetitive code using design patterns where
+ > appropriate, such as Strategy, Observer, or Factory Method. These patterns often help
+ > simplify and standardize solutions to common design problems.
+ >
+ > When you see a pattern of behavior or logic in your code, consider whether a design pattern
+ > could help simplify the code and make it more reusable.
+
+13. **Refactor to Improve Readability**
+
+ > Code should be written in a way that is easy to understand, not just for you, but for
+ > others as well. Readability is key to maintainability.
+ >
+ > If you struggle to understand a piece of code you wrote weeks ago, or if it takes too long
+ > to explain it to a colleague, it's time to refactor for clarity. Simple actions like
+ > renaming variables or reorganizing logic can have a big impact.
+
+14. **Use Testing to Safeguard Refactoring**
+
+ > Testing is a safety net for refactoring. Write comprehensive unit tests before refactoring
+ > to ensure that the behavior remains consistent after changes.
+ >
+ > Once you have written tests, refactor confidently knowing that the tests will catch any
+ > unintended changes in behavior. Always rerun tests after refactoring to ensure nothing breaks.
+
+15. **Refactor Legacy Code Incrementally**
+
+ > Working on legacy code can be daunting, but don’t try to refactor everything at once.
+ > Make small, incremental changes over time.
+ >
+ > Focus on refactoring small, manageable portions of the legacy code every time you work on it.
+ > Over time, these improvements will significantly enhance the code’s quality without
+ > disrupting ongoing development.
+