Tracking changes with context

Imagine an online shop that sells… anything, really. Users place orders buying products they want. But sometimes they regret their decision me 2min after buying something. Sometimes there’s a problem with the payment gateway. Sometimes you have a problem in your warehouse, and the products are out of stock. Sometimes you can’t sell because you can’t ship to the user’s address or any other constraint that can’t be verified at checkout time (please please PLEASE verify everything you can at checkout time, you make everyone’s life easier :)).

All these possibilities result in the order cancellation. Behind the scene, the application updates the order’s status field from processing to canceled and hopefully logs the date/time. That’s it. Right? RIGHT?

Sometimes knowing What changed is not enough. It’s essential to know Why it changed.

If you don’t know why something happened, you are losing insight about your application, making you unable to act upon it.


Example 1: User doesn’t have sufficient funds

Payment declined because the user didn’t have sufficient funds. As a result, the application cancels the order automatically.

Your application’s payment workflow could set the fields gateway and reason and error_code returned by the gateway processor.

{
  "event": "order_cancelled",
  "id": 42,
  "actor_id": null,
  "metadata": {
    "reason": "payment_declined",
    "error_code": "INSUFFICIENT_FUNDS",
    "gateway": "IPayU"
    ...
  }
}

Although in this example actor_id is null, I usually have an application user assigned to every automated action in the application.

Example 2: Can’t ship to the user’s address

The user changed their shipping address after placing the order, and you can’t ship there for whatever reason.

Here, an employee filled reason and description after manually analyzing the problem.

{
  "event": "order_cancelled",
  "id": 42,
  "actor_id": 19,
  "metadata": {
    "reason": "shipping_address",
    "description": "User changed his address, can't ship to the new one."
    ...
  }
}

These are contrived examples. There are better ways to handle these cases than canceling the order.


In the next post I’ll show one way you can do it in Elixir using Ecto and Phoenix.