eventbridge
EventBridge Pipes, and the Lambda I deleted

Amazon EventBridge Pipes became generally available on the first of December. The pitch, delivered on stage, was that you should not have to write glue code to connect two AWS services together.
That is a claim worth testing against the glue code I have actually written, because I have written a lot of it and none of it was interesting.
The shape of the thing
A pipe has four stages, in order: source, filter, enrichment, target. Source and target are required; the two in the middle are optional.
Sources are the polling kind — SQS, Kinesis, DynamoDB Streams, Amazon MQ, MSK and self-managed Kafka. The filter drops events you do not want, using the same pattern syntax EventBridge rules already use. The enrichment step can call a Lambda, a Step Functions workflow, an API destination or API Gateway to transform or enrich an event. Targets are the long list of things EventBridge can already reach.
That is the entire product, and its shape is the argument. Those four stages are what a "just move this over there" Lambda does.
The function it replaces
The one I keep writing looks like this, with the details changed:
export const handler = async (event) => {
for (const record of event.Records) {
const body = JSON.parse(record.body)
if (body.type !== 'membership.activated') continue
await sfn.startExecution({
stateMachineArn: process.env.STATE_MACHINE_ARN,
input: JSON.stringify({ memberId: body.memberId, at: body.occurredAt }),
})
}
}
Read it against the four stages. The Records loop is the source. The continue
is the filter. The JSON.stringify reshape is a transformation. The
startExecution is the target. Everything else is the cost of hosting those four
ideas inside a runtime.
And that cost is not zero. It is a deployment package, a runtime version that goes end-of-life on somebody else's schedule, an IAM role, a log group, a concurrency setting, partial-batch-failure handling that is easy to get subtly wrong, and a dependency manifest that security scanning will have an opinion about. For a function whose logic is one comparison.
The equivalent pipe carries none of that:
resource "aws_pipes_pipe" "activations" {
name = "membership-activations"
role_arn = aws_iam_role.pipe.arn
source = aws_sqs_queue.events.arn
target = aws_sfn_state_machine.onboarding.arn
source_parameters {
sqs_queue_parameters {
batch_size = 10
}
}
filter_criteria {
filter {
pattern = jsonencode({ body = { type = ["membership.activated"] } })
}
}
target_parameters {
input_template = <<EOT
{ "memberId": <$.body.memberId>, "at": <$.body.occurredAt> }
EOT
}
}
No runtime, no package, no log retention decision, no cold start.
Where it stops
The filter is a pattern match. It can test values, prefixes, numeric ranges and presence. It cannot call anything, hold state, or make a decision that depends on another system. The moment the rule is "forward this if the member is on a paid plan", the filter cannot answer and you are into the enrichment step — which is a Lambda, and you are back where you started plus a pipe.
So the honest boundary is: Pipes replace the glue that is a projection of the event. They do not replace the glue that is a decision about the event.
It is also worth being clear that this is not a replacement for an EventBridge bus. A bus is one-to-many — publish once, let many rules match. A pipe is one-to-one, source to target. They solve adjacent problems and the naming does them no favours.
What I will actually do
Not a migration. The functions that already exist work, and rewriting working integrations to remove a Lambda is spending real risk to buy tidiness.
What changes is the default for the next one. When the task is "take what arrives on this queue, keep the ones that look like this, reshape them, and hand them to that" — which is a surprising share of the integrations in any event-driven system — the first thing I reach for is a pipe, and a Lambda has to earn its place by needing to decide something.
The other thing I take from it is a reasonable question to ask of existing code.
Of the functions in an account, how many are a for loop, an if, a reshape and
a call? In the accounts I have looked at, more than I would have guessed before
somebody drew the four boxes.
Written by
Deyan Peev
Founding Engineer · Sofia, Bulgaria


