What I Learned while working on an Elixir App

by Atul Bhosale,

Recently, I was working on writing a background worker using Elixir for one of our clients. This post is about what I learned while writing the worker.

Adding decimal numbers

I tried addition for Decimal type, the same way as we do Integers like:

screenshot1
screenshot1

It raises an Arithmetic error since Decimal has an underlying struct:

[%Decimal{coef: coefficient(), exp: exponent(), sign: sign()}][decimal-struct]

I checked the decimal library & I found that it has an add function to add Decimal or integer values. Hence the addition can be done as follows:

screenshot1

Comparing decimal values

Integer comparison can be done with == operator.

screenshot1

How about Decimal?

screenshot1

It returns false since the value on RHS(Right-hand-side) of == operator is not a decimal.

I tried using Decimal.new as follows:

screenshot1

Based on the warning message I tried using Decimal.from_float & Decimal.cast as follows:

screenshot1

but that too returns false.

I then tried to pass a string to Decimal.new as follows:

screenshot1

and it worked.

Approach using find_in_batches

While working on a business use case to fetch the pending orders & to process them. The code would be as follows:

  defp orders_query() do
    from(order in Order,
      where: order.status == ^@pending
    )
  end

  def perform do
    orders_query()
    |> Repo.all()
    |> Enum.each(fn order ->
       update_order(order)
    end)
  end

The above code will load all the records at once in the memory. I decided to search for something which will be similar to find_in_batches from Rails in Elixir and I found this discussion.

The updated code will be as follows:

  def perform do
      Repo.transaction(fn ->
        orders_query()
        |> Repo.stream()
        |> Enum.each(fn order ->
           update_order(order)
        end)
      end)
  end

I have used Repo.stream which by default fetches the records in batches of 500 & it needs to be wrapped in a transaction.

Recursion

In the above approach, I faced an issue. When the time taken to update the records exceeds the timeout, Ecto would raise a timeout error which I have described here.

I solved it by using recursion as follows:

  @batch_size 500

  def perform do
    remaining_records_count()
    |> iterate_multiple_times()
  end

  defp remaining_records_count do
    orders_query()
    |> Repo.aggregate(:count)
  end

  defp iterate_multiple_times(count) when count <= @batch_size,
    do: make_account_balance_available()

  defp iterate_multiple_times(_count) do
    make_account_balance_available()

    remaining_records_count()
    |> iterate_multiple_times()
  end

In the above code, the iterate_multiple_times/1 is a recursive function which calls itself until there aren't any remaining records.

I hope you will find these learnings helpful while building any app/library using Elixir.

More articles

How Claude's Text Watermark Works (And Where It Breaks Down)

A practical walkthrough of Claude's text watermark, how it hides an invisible signal in word choices, how detection works, and why its limitations fall directly out of the design.

Read more

Agentic Memory: A Practical Guide to How AI Agents Remember

A practical guide to agentic memory, covering working, semantic, episodic, and procedural memory, along with storage, retrieval, distillation, and memory evolution.

Read more

Your competitors are already using AI.
The question is how fast you want to unlock the value.

Don't know where to start?

AI is everywhere but it's unclear which investments will actually move your metrics and which are expensive experiments.

Your data isn't ready

Most AI projects fail at the data layer. Pipelines, quality, access all need work before LLMs can deliver value.

Internal teams are stretched

Your engineers are shipping product. They don't have capacity to also become AI specialists with production-grade experience.

Legacy systems block everything

Aging, undocumented codebases make AI integration slow, risky, and expensive. They need to move first.

Don't worry. We've got you covered.

Start with the audit.