·

Workflow Versioning with Git

Workflow Versioning with Git — a complete, field-tested reference by John Kihiu.

John Kihiu12 min read

Workflow Versioning with Git is the work that turns a collection of business systems into a coherent operation. ERP, CRM, e-commerce, payment, marketing, support — each is a tool. The workflow is what connects them. This guide is the field-tested pattern for this workflow tool in a business context.

What is workflow automation

Workflow automation is the discipline of moving data between systems, transforming it, and triggering actions — without a human in the loop for the routine cases. The three categories:

Automate the routine, escalate the exception

The 80/20 of workflow automation: 80% of the value comes from automating the 20% of cases that are routine. The other 80% of cases — the exceptions — need a human. A good workflow automation routes exceptions to a human, with all the context.

The tool landscape

The tools you can choose from, in increasing order of code involvement:

ToolUse whenTrade-off
Zapier, Make, n8nSimple integrations, no-codeFast to build, hard to scale
Workato, Tray.io, BoomiEnterprise iPaaS, governanceExpensive, but with audit and SLAs
Custom code (Laravel, .NET, Node)Complex logic, performance-criticalFull control, full responsibility
Acumatica Business EventsERP-native automationBuilt-in, no external dependency

The patterns that work

1. Webhook-first

For event-driven workflows, use webhooks. The source system pushes; the destination listens. Polling is the tax you pay when webhooks are not available.

2. Idempotency

Every event handler must be idempotent. The same event can fire twice (webhook redelivery, retry on timeout). The handler must recognise the second call and skip the work.

PYTHON · IDEMPOTENT HANDLER
import hashlib
from redis import Redis

redis = Redis()

def handle_event(event_id: str, event_body: dict):
    # Compute a deterministic key for this event
    key = f"event:{hashlib.sha256(event_id.encode()).hexdigest()}"
    # Try to claim the event
    if not redis.set(key, "processing", nx=True, ex=3600):
        # Already claimed — skip
        return {"status": "already-processed"}
    # Process
    do_the_work(event_body)
    redis.set(key, "done", ex=86400)
    return {"status": "ok"}

3. Saga / compensation

For multi-step workflows (e.g. Acumatica invoice + payment + email), the steps are not atomic. If step 3 fails after step 1 succeeded, you have an inconsistent state. The pattern: each step has a compensation (an undo). The saga runs the steps; if a step fails, the compensations run in reverse.

PYTHON · SAGA PATTERN
class InvoiceSaga:
    def execute(self, order):
        invoice = self.create_invoice(order)
        try:
            payment = self.create_payment(order, invoice)
        except Exception:
            self.cancel_invoice(invoice)  # compensate
            raise
        try:
            self.send_email(order, invoice, payment)
        except Exception:
            self.refund_payment(payment)   # compensate
            self.cancel_invoice(invoice)   # compensate
            raise

For the broader saga patterns, see the saga pattern guide.

4. Dead-letter queue

For workflows that fail repeatedly (an integration that is down), the events cannot be lost. The pattern: dead-letter queue. Failed events go to a queue (or table) instead of being dropped; a worker replays them after the integration is fixed.

PYTHON · DEAD-LETTER HANDLER
def handle_with_dlq(event):
    try:
        process(event)
    except Exception as e:
        attempts = event.get("attempts", 0) + 1
        if attempts >= 5:
            # Send to dead-letter
            dead_letter_queue.push(event, str(e))
        else:
            # Retry with exponential backoff
            delay = 2 ** attempts
            retry_queue.push(event, delay=delay, attempts=attempts)

Monitoring the workflow

A workflow that fails silently is worse than a workflow that does not exist. The monitoring:

For the broader monitoring patterns, see the monitoring guide.

Wrapping up

The tools, the patterns, the safety, the monitoring. Get all four right and the workflow runs itself. Skip any one and you are debugging at 2 AM. The discipline is the same as any production system — fail loudly, fail safely, and have a runbook for when the automation breaks.

Wrapping up

That is the working approach I use on Acumatica projects. The same patterns show up whether you are in Nairobi, Johannesburg, Kigali, Lusaka or Harare — and they are the things that keep work moving when an upgrade lands at 6 PM on a Friday. If you are stuck on something specific, reach out or keep reading through the rest of the Acumatica blog.

John Kihiu
John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.