# Backend developer: the queue, the database and the server behind the API

> What I do behind the API — contracts first, delivery guarantees that queues do not give you, and index decisions made by measurement.

- Technologies: RabbitMQ, Redis, PostgreSQL, MySQL, Elasticsearch, Docker, nginx, GitHub Actions
- Experience: Since 2010
- Posts: 26
- Projects: 3
- Updated: 2026-09-02
- Source: https://www.muhammetsafak.com.tr/en/expertise/backend/
- Language: en-US
- Author: Muhammet Şafak

---
## Me in this ecosystem

Backend development, for me, is not a language but a boundary of responsibility:
everything behind the screen the user sees. Contract, queue, database and server
are four faces of the same work, and a decision about one binds the other three.

Observability, infrastructure managed as code and rollback strategy I write
about as architectural subjects on [sade.dev](https://sade.dev).

### What I do on the backend

What they have in common is sitting behind the screen the user sees: the contract, the queue, the database and the server.

- **The API contract** — The response shape, the error contract and the versioning decision are settled before code is written. Otherwise every client team writes its own assumption, and that assumption becomes your problem six months later.
- **The asynchronous delivery path** — Queue, consumer and outbox relay; the structure that takes the synchronous wait out from in front of the user. The second half of the same discipline is idempotency: a request that can be repeated without side effects.
- **Database decisions** — Index and plan behaviour is chosen by measurement, not intuition; intuition works particularly badly here. The measurement itself does not live on this page but in the research ledger, together with its method and environment.
- **Search and the server layer** — Elasticsearch at the boundary where relational stops being enough; the environment with Docker, the deploy pipeline with GitHub Actions, nginx in front. The scope stays at application level.

### A queue does not guarantee delivery

The mistake I see most often is assuming work becomes durable the moment it moves onto a queue. The chain is read in order; each step closes what the previous one left open.

1. **Writing and publishing are two systems** — There is no atomicity between an INSERT into the database and a publish to the queue. If the write succeeds and the publish drops on a network hiccup, the row exists and the event never left.
2. **The message is written in the same transaction** — The message is not published directly; it is written as a row into an outbox table inside the same transaction. The record and the message then share a single fate.
3. **A relay publishes the rows** — A separate relay reads those rows and hands them to the queue. This is where the message stops getting lost — but not where the guarantee is complete.
4. **An idempotent consumer completes it** — The outbox gives you at-least-once: the message is not lost, but it can repeat. Writing every processed event id into a processed_messages table inside the same transaction makes that repeat harmless.

### My scope on data and the server

I do not make database decisions on intuition, and on the server side the scope stays at application level.

- **Index decisions made by measurement** — Index choices, query plans and behaviour that shifts as a table grows are selected by measuring. A decision made without it can slow tomorrow the query it sped up today.
- **A full-text search layer** — It takes over where the relational store stops being enough. Stemming, synonym expansion and relevance ranking exist there too; what brings in a separate layer is not their absence but how far they carry at scale and across languages.
- **The server at application level** — Packaging the runtime environment, setting up the test and deploy pipeline, configuring the layer in front of the application.

### The order that makes change safe

The hard part of back-end work is not writing a new feature but changing a running system without breaking it.

1. **Plan the schema around the window where both versions are up** — Old and new code run side by side for a while; a schema change written without accounting for that window always breaks the running system first.
2. **Split the data move into reversible steps** — A move that runs in one go has no way back. One split into steps can stop at the step that goes wrong.
3. **Keep the test surface in three separate places** — Where the business rule is exercised, where the outside boundary is imitated, and where the flow runs end to end. The cost of a change has to stay smaller than the relief it brings.

### I do not cover infrastructure as architecture here

Observability, IaC and rollback strategy do not stand on their own on this page; my scope here stays at application level. The place I write infrastructure as an architectural subject is sade.dev.

## Frequently asked

### How long have you worked on the backend?

Since 2010, so 16 years. Two years after I started with PHP the weight of the work moved from the contract to the queue and stayed there; that period is where the 26 posts on this site came from.

### What do people usually come to you with on the backend?

Most often it is "we moved to a queue, but some jobs vanish." The answer is nearly always in the same place: writing to the database and publishing to the queue are two separate systems with no atomicity between them.

### Where do you start when designing an API?

With the contract. If the response shape, the error contract and the versioning decision are not settled before code is written, every client team writes its own assumption instead. A good share of the 26 posts on this site circle those three headings.

### Does using a queue guarantee delivery?

No. A transactional outbox stops messages from being lost, but what it gives you is at-least-once — the message can repeat. What completes the guarantee is not the queue; it is an idempotent consumer.

### How do you make index decisions?

By measuring. Choosing a partial index purely because it is "small and fast" is a trap: the moment a generic plan takes over, the partial index becomes unusable. The good news is that the cliff is fenced — Postgres does not make that switch on its own; you have to force the setting by hand. The full measurement sits in the research ledger.

### Do you set up the server side too?

Yes, at application level. I set up the environment with Docker, the deploy pipeline with GitHub Actions and nginx in front. Infrastructure as an architectural subject is written on sade.dev.

### How many projects run on this stack?

3 of the projects recorded on this site sit directly on this queue-and-database stack.
