Skip to content

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

  • Building our own infra using our office hardware

    How we turned a Mac mini and a stack of MacBooks into a self-hosted k3s cluster, moved our apps off a managed platform, and run it all over Tailscale.

    Read more →
  • What Is an AI Harness?

    A harness is the code that sits around an LLM and lets it actually do things: read files, run commands, search the web. The model writes text. The harness turns that text into actions, and the results back into text.

    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.

Book a free discovery call