Elixir:
Refactoring

How to:

Let’s tidy up a common Elixir pattern. We’ll refactor a function calculate_stats that does more than it should by breaking it into smaller, reusable pieces.

defmodule Stats do
  # Original, unrefactored code
  def calculate_stats(data) do
    total = Enum.sum(data)
    count = Enum.count(data)
    mean = total / count
    {mean, total}
  end
  
  # Refactored code
  def calculate_mean(data), do: Enum.sum(data) / Enum.count(data)
  
  def calculate_total(data), do: Enum.sum(data)
  
  def calculate_stats_refactored(data) do
    mean = calculate_mean(data)
    total = calculate_total(data)
    {mean, total}
  end
end

# Sample Output
# Before Refactoring
Stats.calculate_stats([1, 2, 3])
# => {2.0, 6}

# After Refactoring
Stats.calculate_stats_refactored([1, 2, 3])
# => {2.0, 6}

As you can see, the output remains the same, but now we have modular functions that can be reused and tested independently.

Deep Dive

Refactoring isn’t a new concept; it’s been a crucial part of programming since the early days of software development. Notable works, such as Martin Fowler’s “Refactoring: Improving the Design of Existing Code,” provide foundational practices for refactoring with insights into when and how to apply them.

Alternatives to manual refactoring include automated code analysis tools, which can suggest or even perform refactorings. However, automated tools may not always grasp the full context of the code and can miss subtleties that a human reviewer would catch.

Implementation details in Elixir specifically include understanding the functional paradigm and leveraging pattern matching, guard clauses, and the pipe operator to write clear and concise code. For instance, refactoring often involves converting complex imperative-style functions into smaller, composable functions that follow Elixir’s preference for immutability and side-effect-free operations.

See Also

For more on Elixir-specific refactoring techniques: