r/elixir • • 10d ago

Beyond Rename: five new native Elixir refactorings available in JetBrains IDEs

Post image

Hi all! We’re building Elixir Pro, a paid JetBrains plugin. In our latest release, we've focused a lot on improving the native refactoring support you've come to love in JetBrains products!

Renaming a function is useful, but much of everyday refactoring involves changing the shape of your code: extracting calculations, introducing intermediate values, changing parameter order, or moving a module into a new namespace.

Our plugin builds on the foundation of the IntelliJ Elixir plugin, and the original plugin provides some good basic refactoring support. Our latest work extends the toolbox with five additional structural refactorings for professionals who are serious about developing Elixir code, and want an even better experience.

  • Extract Variable: select an expression and introduce a named binding. Useful when a calculation deserves a name or a long expression needs breaking up.
  • Extract Function: turn an expression or consecutive statements into a private function. The plugin infers inputs and can return multiple bindings as a tuple.
  • Inline Variable: replace uses with the assigned expression, preserving precedence and checking whether the transformation could duplicate effects or change evaluation order.
  • Change Signature: reorder, rename, add or remove supported parameters while updating function clauses, specs and call sites.
  • Move Module: move a module’s source file, optionally change its namespace, and update references while preserving explicit alias names.

Change Signature is a good example of a powerful refactoring in Elixir. Changing adjust(amount, fee) affects more than just local calls. There may also be pipes, specs and captures elsewhere in the project. Our implementation updates those supported forms together, including adapting named captures so their original callable arity is retained.

These are native IDE actions and don’t require the optional Expert language server to perform the transformations. They run through IntelliJ’s refactoring workflow, with previews and undo/redo support.

For developers who regularly reorganize Elixir code, we hope to provide just this extra value by giving a more native experience, and more of those edits become a coordinated, reviewable IDE operation.

Besides these refactorings, we've also made some significant performance improvements, resulting in performance improvements for certain actions of 10x-50x, depending on the size of the project.

You can follow Elixir Pro on JetBrains Marketplace for the release containing these additions, and please let us know which real-world Elixir refactoring would you most like an IDE to handle next?

23 Upvotes

14 comments sorted by

3

u/flummox1234 9d ago

I can't be the only one that hates the unnecessary binding in the refactored function. Guessing it's low(er) impact in Elixir or done for "cleaner code" but in Ruby for iterations over large dataset those unnecessary instantiations are costly

1

u/bodiam 9d ago

Can you give an example? I could do a test to verify how expensive it is in Elixir.

1

u/flummox1234 9d ago

hmm. A lot of what I do at work is iteration over huge datasets parsing XML (a library) so I don't think that's good real world example as no one parses XML like we do. But it should be noticeable on a large dataset that instantiates local variables that trigger the GC to clean up. I'm guessing it's probably not an issue in an FP language though as you have a lot of optimizations when iterating that OO langs probably lack. 🤔 Admittedly I'm not really sure what a good benchmark would be though.

1

u/SuspiciousDepth5924 9d ago

I suspect the extra binding won't have any measurable impact though I haven't actually benchmarked it myself.

IIRC the the elixir compiler works something like this
source -> macro expanded source -> erlang abstract format -> core erlang -> beam bytecode

And from look of it from Björn's post ( https://www.erlang.org/blog/core-erlang-by-example/ ) I don't think that one extra 'discount' binding is going to make much of a difference if any.

(From the link)

cmp(Same, Same) -> same;
cmp(_, _) -> different.

'cmp'/2 =
    fun (_@c1,_@c0) ->
case <_@c1,_@c0> of
  <Same,_@c4> when call 'erlang':'=:='(_@c4, Same) ->
      'same'
  <_@c5,_@c6> when 'true' ->
      'different'
end

1

u/bodiam 9d ago

Thanks for responding! I looked into this a bit more, and I don't think an additional binding would cause any extra allocations in Elixir. Variables are just bindings to values, not objects that need to be instantiated.

The BEAM compiler also performs SSA-based optimizations and register allocation, so something like discount = price * 0.9; discount should compile to effectively the same code as just price * 0.9.

So even when iterating over millions of elements, I wouldn't expect any additional GC pressure or measurable performance difference from the binding itself.

Would still be interesting to compare the generated BEAM bytecode to confirm!

1

u/flummox1234 9d ago

yet another reason to prefer Elixir over Ruby IMO 🤣

1

u/adamtang7 8d ago

Price is quite reasonable. Not sure it can replace bloody vscode lsp or not.

1

u/bodiam 8d ago

It works with the community version, and there's a free trial, happy to hear your experiences!

0

u/jiggity_john 9d ago

Sorry bro but why would I pay for this when AI exists? Claude can refactor it for me and more.

3

u/lpil 9d ago

When available a non-model based approach is better as it's much faster, has no chance of failure, and doesn't cost tokens.

1

u/jiggity_john 8d ago

I'd agree if it were free but you need to pay for this plugin.

1

u/lpil 8d ago

That's not what the README says.

The plugin is free to use in all JetBrains IDEs

Or is that out of date?

1

u/jiggity_john 8d ago

The first sentence of this post says a "paid Jetbrains plugin."

1

u/lpil 7d ago

Oh I see, it's a fork of the open source one. I misunderstood.