Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 7 min read

The Connector Was Never the Work: Five Questions I Ask Before Estimating an Integration

Every integration ticket I have been handed started with the same sentence: we just need this system to talk to that one. It reads like a connector. It gets estimated like a connector. Then it turns into a project, becau

Every integration ticket I have been handed started with the same sentence: we just need this system to talk to that one. It reads like a connector. It gets estimated like a connector. Then it turns into a project, because the connection was never the work.

The HTTP call is the easy part. What eats the weeks is unspecified behavior: what happens when both sides edit the same record, when the receiving system rejects a row for a validation rule nobody mentioned, and when a human edits by hand the field your automation is also writing. Those are business decisions wearing technical clothes.

This is the checklist I run before estimating one, adapted from a guide I wrote for my studio's clients.

First, name the shape

There are three shapes, and they cost very different amounts.

A one-way feed. Data goes from A to B and never comes back. Orders into accounting, leads into a CRM, events into a warehouse. Cheapest shape, because there is nothing to reconcile.

A two-way sync. Both systems can change the same record and both changes must survive. This is where the money goes. Every field needs an owner, every conflict needs a rule, and you have to stop the two systems bouncing the same update back and forth forever. Most "sync" requests turn out to be a one-way feed plus two or three fields that genuinely go both ways. Finding that out early is worth weeks.

A process that spans systems. A new order triggers a credit check, a stock reservation, a notification and an invoice across four tools, with conditions at each step. That is not a connection. It is a small application whose interface happens to be other people's software, and it should be scoped like one.

Most of my voice agent work is the third kind. The agent on the phone is the visible part; the value is where the outcome lands.

The five questions that size it

1. What does each end actually give you?

Two documented APIs with a sandbox is the good case. In practice you meet an API that covers the reads but not the writes, a rate limit that quietly caps how fast you can backfill history, or no API at all.

With no interface, the options are a file exchange, a read replica, the vendor's export, or, at the very bottom, scripting the UI, which is fragile and should be called fragile out loud. I confirm sandbox access and rate limits before agreeing to any date, because both routinely change the plan.

2. Who owns which field?

Source of truth is per field, not per system. The CRM owns the phone number. The billing platform owns the payment terms. Where two systems both let a human edit the same field, you either pick a winner or write a rule. Leaving it unstated is how you get a sync that flaps between two values while each team insists its system is right.

This shows up in code more literally than people expect. In VoiceDash, the Retell webhook upserts a call into a Conversation row, but the update block deliberately leaves out tags, notes, summaries and evaluations. Those fields belong to the analysis step and to the humans using the portal, and a late webhook must never overwrite them. I wrote up the full version of that in Undefined Is Not Null. The field map was not a document. It was which keys I allowed into an update: {} object.

3. How fresh does the data have to be?

Nightly batch and event-driven within seconds are different builds, not a config flag. Real time brings webhooks, queues, ordering questions and a replay path for events you missed while you were down. So ask what actually breaks if the data is an hour old. Often nothing.

Set freshness per flow, not per project. And if you do go event-driven, remember that a webhook tells you something happened, not that you have everything. You will want a backfill path anyway.

4. How is the same thing recognized on both sides?

Two systems almost never share an identifier. The same customer is a record in one and a contact in the other, matched on an email that has since changed or a phone number stored in three formats. You need an explicit matching rule, and you need a place for the records that do not match, with a person who checks it.

Data that does not reconcile is worse than missing data, because people keep trusting it. The accounting software I build at Null Studio lives and dies on this; more in A Stored Balance Is a Rumor.

5. What happens when a transfer fails?

Not if. A destination will be down, a record will be rejected, a credential will expire. The difference between an integration that survives and one that quietly stops is the failure path:

  • retries with sensible backoff
  • idempotency, so a retry does not create a second invoice
  • a queue that holds what did not go through
  • an alert that reaches a named human
  • a reconciliation report somebody actually reads

The cheapest idempotency I know is a unique constraint on the other system's ID. VoiceDash keys conversations on @@unique([agentId, platformCallId]) and upserts against it, so a webhook delivered twice lands on the same row, and the backfill job that re-reads calls from Retell looks rows up by the same key instead of inserting:

await prisma.conversation.upsert({
  where: {
    agentId_platformCallId: { agentId: agent.id, platformCallId: call.call_id },
  },
  create: { /* full row */ },
  update: { /* only the fields this source owns */ },
});

It also writes a raw WebhookLog row before parsing anything, so when a payload surprises me I can see what actually arrived instead of guessing.

That failure path is roughly half the engineering, and it never appears in the estimate. Someone also has to watch the exceptions after launch; monitoring voice agents in production is the same problem with phone calls attached.

When no-code is the right answer

I build custom integrations for a living and still recommend no-code tools often. If volume is modest, the mapping is simple, and a missed record costs an apology rather than money, use one.

The signs you have outgrown it:

  • Volume. Per-task pricing that was trivial at a hundred records a day is a line item at fifty thousand.
  • The logic became a program. Branches, loops and lookups in a visual editor are still software, just without tests, version control or review.
  • Error handling beyond a red icon. Retries, ordering, partial failure and reconciliation are engineering.
  • The data cannot go through a third party. Regulated or contractual data often settles it on its own.
  • Nobody can safely change it. The person who built it left, and now it runs a process nobody wants to touch.

The sensible middle: no-code at the edges for low-stakes notifications, a properly built spine.

What AI changes, and what it does not

The build side compresses a lot. Field mapping, transformation code, adapters, retry handling and the pile of tests an integration needs are the mechanical work coding agents do well under review.

Some integrations get replaced by an agent that just does the task. Careful with that framing: an agent that reads your CRM, checks availability and books an appointment is an integration project wearing new clothes. All five questions still apply, and two apply harder because nobody is clicking the button: idempotency, so a retried booking does not become two, and permissions, so it can only touch what it should. I argued the second one in Give an AI Agent Write Access One Verb at a Time.

What AI does not compress is deciding who owns which field and how fresh it must be. That needs the people who run the operation, and they are usually the critical path.

How I scope one now

  1. Read both ends' docs before estimating.
  2. Write the field map. Every field that moves, which direction, how often, and which side wins a conflict. That document is the project.
  3. Set freshness per flow.
  4. Design the failure path first. Retries, idempotency, a reject queue, one named person on the alert.
  5. Build the thinnest end-to-end slice with real data. One record type, all the way through, in production conditions.
  6. Run it next to the manual process and reconcile before anyone switches the manual step off. The diff finds the rules nobody wrote down, which is the same lesson as rewrites failing in month seven.

An integration is not a wire between two boxes. It is an agreement about who owns what, how fast it travels and what happens when it does not arrive. Make those decisions on purpose and you get a boring system that runs for years. Skip straight to the connector and you get one that works in the demo and needs someone watching it forever.

What is the worst field-ownership fight you have been dragged into? I would love to hear how it got settled.

The original, client-facing version of this guide lives on the Null Studio blog.

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.