Home

Awesome

Catalog of Elixir Refactorings

GitHub last commit Twitter URL

Table of Contents

Introduction

Elixir is a functional programming language whose popularity is on the rise in the industry <sup>link</sup>. As no known studies have explored refactoring strategies for code implemented with this language, we reviewed scientific literature seeking refactoring strategies in other functional languages. The found refactorings were analyzed, filtering only those directly compatible or that could be adapted for Elixir code. As a result of this investigation, we have initially proposed a catalog of 55 refactorings for Elixir systems.

Afterward, we scoured websites, blogs, forums, and videos (grey literature review), looking for specific refactorings for Elixir that its developers discuss. With this investigation, the catalog was expanded to 76 refactorings. Finally, 6 new refactorings emerged from a study mining software repositories (MSR) performed by us, so this catalog is constantly being updated and currently has 82 refactorings. These refactorings are categorized into four different groups (Elixir-specific, traditional, functional, and Erlang-specific), according to the programming features required in code transformations. This catalog of Elixir refactorings is presented below. Each refactoring is documented using the following structure:

Note: (*) not all refactorings have explicit definitions for these fields.

This catalog of refactorings aims to improve the quality of code developed in Elixir, helping developers promote the redesign of their code, making it simpler to understand, modify, or even improving performance. These transformations must be performed without changing the original behavior, thus preserving the code's functionality. For this reason, we are interested in knowing Elixir's community opinion about these refactorings: Do you agree that these refactorings can be useful? Have you seen any of them in production code? Do you have any suggestions about some Elixir-specific refactorings not cataloged by us?...

Please feel free to make pull requests and suggestions (Issues tab). We want to hear from you!

▲ back to Index

Elixir-Specific Refactorings

Elixir-specific refactorings are those that use programming features unique to this language. In this section, 14 different refactorings classified as Elixir-specific are explained and exemplified:

Alias expansion

▲ back to Index


Default value for an absent key in a Map

▲ back to Index


Defining a subset of a Map

▲ back to Index


Modifying keys in a Map

▲ back to Index


Simplifying Ecto schema fields validation

▲ back to Index


Pipeline using "with"

▲ back to Index


Pipeline for database transactions

▲ back to Index


Transform nested "if" statements into a "cond"

▲ back to Index


Explicit a double boolean negation

▲ back to Index


Transform "if" statements using pattern matching into a "case"

▲ back to Index


Moving "with" clauses without pattern matching

▲ back to Index


Remove redundant last clause in "with"

▲ back to Index


Replace "Enum" collections with "Stream"

▲ back to Index


Generalise a process abstraction

▲ back to Index


Traditional Refactorings

Traditional refactorings are those mainly based on Fowler's catalog or that use programming features independent of languages or paradigms. In this section, 25 different refactorings classified as traditional are explained and exemplified:

Rename an identifier

▲ back to Index


Moving a definition

▲ back to Index


Add or remove a parameter

▲ back to Index


Grouping parameters in tuple

▲ back to Index


Reorder parameter

▲ back to Index


Extract function

▲ back to Index


Inline function

▲ back to Index


Folding against a function definition

▲ back to Index


Extract constant

▲ back to Index


Temporary variable elimination

▲ back to Index


Extract expressions

▲ back to Index


Splitting a large module

▲ back to Index


Remove nested conditional statements in function calls

▲ back to Index


Move file

▲ back to Index


Remove dead code

▲ back to Index


Introduce a temporary duplicate definition

▲ back to Index


Introduce overloading

▲ back to Index


Remove import attributes

▲ back to Index


Introduce import

▲ back to Index


Group Case Branches

▲ back to Index


Move expression out of case

▲ back to Index


Simplifying checks by using truthness condition

▲ back to Index


Reducing a boolean equality expression

▲ back to Index


Transform "unless" with negated conditions into "if"

▲ back to Index


Replace conditional with polymorphism via Protocols

▲ back to Index


Functional Refactorings

Functional refactorings are those that use programming features characteristic of functional languages, such as pattern matching and higher-order functions. In this section, 32 different refactorings classified as functional are explained and exemplified:

Generalise a function definition

▲ back to Index


Introduce pattern matching over a parameter

▲ back to Index


Turning anonymous into local functions

▲ back to Index


Merging multiple definitions

▲ back to Index


Splitting a definition

▲ back to Index


Inline macro

▲ back to Index


Transforming list appends and subtracts

▲ back to Index


From tuple to struct

▲ back to Index


Struct guard to matching

▲ back to Index


Struct field access elimination

▲ back to Index


Equality guard to pattern matching

▲ back to Index


Static structure reuse

▲ back to Index


Simplifying guard sequences

▲ back to Index


Converts guards to conditionals

▲ back to Index


Widen or narrow definition scope

▲ back to Index


Introduce Enum.map/2

▲ back to Index


Merging match expressions into a list pattern

▲ back to Index


Function clauses to/from case clauses

▲ back to Index


Transform a body-recursive function to a tail-recursive

▲ back to Index


Eliminate single branch

▲ back to Index


Transform to list comprehension

▲ back to Index


Nested list functions to comprehension

▲ back to Index


List comprehension simplifications

▲ back to Index


Closure conversion

▲ back to Index


Replace pipeline with a function

▲ back to Index


Remove single pipe

▲ back to Index


Simplifying pattern matching with nested structs

▲ back to Index


Improving list appending performance

▲ back to Index


Convert nested conditionals to pipeline

▲ back to Index


Replacing recursion with a higher-level construct

▲ back to Index


Replace a nested conditional in a "case" statement with guards

▲ back to Index


Replace function call with raw value in a pipeline start

▲ back to Index


Erlang-Specific Refactorings

Erlang-specific refactorings are those that use programming features unique to the Erlang ecosystem (e.g., OTP, typespecs, and behaviours). In this section, 11 different refactorings classified as Erlang-specific are explained and exemplified:

Typing parameters and return values

▲ back to Index


Moving error-handling mechanisms to supervision trees

▲ back to Index


From meta to normal function application

▲ back to Index


Remove unnecessary calls to length/1

▲ back to Index


Add type declarations and contracts

▲ back to Index


Introduce processes

▲ back to Index


Remove processes

▲ back to Index


Add a tag to messages

▲ back to Index


Register a process

▲ back to Index


Behaviour extraction

▲ back to Index


Behaviour inlining

▲ back to Index


About

This catalog was proposed by Lucas Vegi and Marco Tulio Valente, from ASERG/DCC/UFMG.

For more info see the following paper:

Please feel free to make pull requests and suggestions (Issues tab).

▲ back to Index

Acknowledgments

Our research is part of the initiative called Research with Elixir (in portuguese). We are supported by Dashbit and Rebase, which are companies that support this initiative:

<div align="center"> <a href="https://dashbit.co/" alt="Click to learn more about Dashbit!" title="Click to learn more about Dashbit!"><img width="23%" src="https://github.com/lucasvegi/Elixir-Refactorings/blob/main/etc/dashbit_logo.png?raw=true"></a> <br><br> <a href="https://rebase.com.br/" alt="Click to learn more about Rebase!" title="Click to learn more about Rebase!"><img width="23%" src="https://github.com/lucasvegi/Elixir-Refactorings/blob/main/etc/rebase_logo.png?raw=true"></a> <br><br> </div>

We were also supported by Finbits, a Brazilian Elixir-based fintech that is a supporter of this initiative:

<div align="center"> <a href="https://www.finbits.com.br/" alt="Click to learn more about Finbits!" title="Click to learn more about Finbits!"><img width="15%" src="https://github.com/lucasvegi/Elixir-Code-Smells/blob/main/etc/finbits.png?raw=true"></a> <br><br> </div>

▲ back to Index

<!-- Links --> <!-- [ICPC-ERA]: https://conf.researchr.org/track/icpc-2022/icpc-2022-era [preprint-copy]: https://doi.org/ [ICPC22-PDF]: https://github.com/lucasvegi/Elixir-Code-Smells/blob/main/etc/Code-Smells-in-Elixir-ICPC22-Lucas-Vegi.pdf [ICPC22-YouTube]: https://youtu.be/3X2gxg13tXo [Podcast-Spotify]: http://elixiremfoco.com/episode?id=lucas-vegi-e-marco-tulio -->

Footnotes

  1. This refactoring emerged from a Grey Literature Review (GLR). 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

  2. This refactoring emerged from a Mining Software Repositories (MSR) study. 2 3 4 5

  3. This refactoring emerged from an extended Systematic Literature Review (SLR).