A backend engineer builds the part of an application that runs on a server: the data, the rules, and the interface other programs call to reach both.
That sentence is true and almost useless. It describes what you produce, not what the job feels like or what you are actually held responsible for. This post is the useful version — the job described by what lands in your queue.
What you own
Four things. Everything else is negotiable; these are yours.
1. The contract
Other people's code calls yours. A web app, a mobile app, another team's service, a scheduled job. What you expose — the URLs, the fields, the status codes, the errors — is a promise, and once someone depends on it you cannot quietly change it. Designing that promise so it survives the next six features is a real skill and it is post 5 of this track.
2. The data
You decide what gets stored, in what shape, and what "correct" means for it. A frontend bug shows a wrong number and you refresh the page. A backend bug writes a wrong number and it is still wrong next year. Data outlives every other decision in the system, including the language it was written in.
3. Correctness when things happen at once
This is the part that separates backend work from most other programming, and it is rarely taught. Your code does not run once. It runs a thousand times concurrently, on several machines, and two of those runs are about to touch the same row.
The classic version: two people click "buy" on the last item at the same instant. Both requests read stock = 1, both decide it is fine, both write stock = 0, and you have sold two of something you had one of. Nothing crashed. No test failed. You will find out from a customer.
4. What happens at 3am
If it breaks at night, someone has to be able to tell what broke and why. That is not an ops concern bolted on afterwards — it is a property of code you either wrote in or did not, and it is post 10.
Where the line is
Titles vary wildly between companies. Roughly:
| They handle | You handle |
|---|---|
| Frontend — what it looks like, what happens on click, browser state | What the data is, and the API they call to get it |
| DevOps / SRE — the cluster, the pipeline, the network, the alerts | An app that can be deployed, configured and observed by those tools |
| DBA (if there is one) — backups, replication, tuning the server | The schema, the indexes your queries need, and the queries themselves |
| Data engineering — warehouses, pipelines, analytics | The transactional data they read from |
| QA — exploratory and end-to-end testing | Unit and integration tests, which are part of the change, not a later step |
At a small company you are all of these. That is not a warning — it is the fastest way to learn, because you get to see which decisions cost you later.
A real ticket, start to finish
"Customers should be able to check out without creating an account."
Guest checkout. Sounds like a frontend change. It is almost entirely backend, and it touches most of this track:
- Ask what it means. If there is no account, where does the confirmation email go? Answer: the request must carry one. That is a new required field, and now it is a schema question.
- Change the model. An order currently points at a user. Now it points at a user or carries a guest email — and exactly one of the two must be present.
- Change the security rules.
POST /api/ordershas to stop requiring a token. Order history must keep requiring one, or every guest sees everybody's orders. - Validate. A missing guest email is a 400 with a message a human can read, not a null pointer four layers down.
- Write the migration. The column has to be added to a table with live rows in it, without downtime.
- Test both paths. Signed-in checkout still works; guest checkout works; a guest cannot read someone else's order.
- Deploy and watch the error rate.
Here is step 2 and step 4 as they actually appear in the demo app — the whole "one of the two" rule, in the first lines of the method that creates an order:
@Override
@Transactional
public OrderCreateResponseDTO createOrder(OrderCreateDTO dto, String userEmail) {
// Guest checkout is the null-user path. A signed-in order attaches the account instead.
User user = userEmail == null ? null : userDAO.findByEmail(userEmail).orElse(null);
if (user == null && (dto.guestEmail() == null || dto.guestEmail().isBlank())) {
throw ApiException.badRequest("An email address is required to check out as a guest");
}
if (dto.orderType() == OrderType.DELIVERY
&& (dto.addressLine1() == null || dto.addressLine1().isBlank())) {
throw ApiException.badRequest("A delivery address is required for delivery orders");
}
// Every figure below comes from the database, never from the request.
PricingService.PricedOrder priced = pricingService.price(dto);
...
}Four lines of business rule, stated once, in the place that cannot be bypassed. Note the last comment. That is step 4 of the ticket nobody wrote down, and it is the difference between an e-commerce API and a free pizza dispenser.
The rule that comment is protecting
Never trust the client. Not the browser, not the mobile app, not the other team's service — even when your own team wrote all three.
A request is just bytes on a socket. Anybody can send any bytes. If the price of a pizza arrives
in the request body, then the price of a pizza is whatever the sender says it is, and someone will
eventually notice. In the demo app the request chooses which product and which
size; every figure is then read from the product_size table by the server.
The general form: the client chooses, the server decides. Identity, prices, permissions, timestamps and ids are all server-side facts. Validation in the browser is a courtesy to honest users; it is not a security control, because it runs on a machine you do not own.
What the day actually looks like
Less typing than you expect. A rough shape:
- Reading code — most of your time, most days. You will spend far longer understanding a system than adding to it.
- Working out what was actually asked for. The guest checkout ticket above was one sentence and had at least three unstated decisions in it. Finding those before you build is the highest-value thing you do all week.
- Writing and testing the change.
- Reviewing other people's changes, and having yours reviewed.
- Investigating something that is behaving oddly in production, usually with logs and a database client.
What to remember
- You own the contract, the data, correctness under concurrency, and being able to debug it at 3am.
- Data outlives everything. Get the shape right before you get the feature done.
- The client chooses; the server decides. Prices, permissions and identity are never taken from a request.
- Concurrency is the thing that will surprise you. Your code runs many times at once, and two of those runs are about to touch the same row.
- A one-sentence ticket usually hides three decisions. Ask before you build.
Next: the Java you actually need before touching a framework.