Backend Service Engineering
Turn completed Python and SQL foundations into an understandable service by tracing each request through HTTP, domain decisions, durable data, security, background work, failure, and recovery.
HTTP services and API boundaries
Objective Run one complete local request and explain how a client message becomes a visible 201 or 409 response.
Core explanation
A backend service is a running program that receives requests and sends responses. A client is the program that starts the conversation. An HTTP request contains a method, path, headers, and sometimes a body. The method names the kind of action, the path names the target, headers describe the message, and the JSON body carries structured input. The server routes the request, validates the message, makes one application decision, and returns an HTTP status plus a JSON response. A status is a machine-readable summary: 201 means a resource was created, while 409 means the request conflicts with current state. The first laboratory uses Python’s standard library and one in-memory set so the complete boundary is visible without installing a framework or database. That set is a teaching adapter and loses all enrollments when the process stops; it is not durable storage. The public contract is what the client may depend on, including the method, path, accepted input, status, headers, response shape, and failure meanings. Later chapters replace the teaching adapter with validated layers, transactions, authorization, reliable work, bounded operations, and recovery evidence.
Start one real service without installing a framework
Create an empty `backend-boundary` directory. Copy the example into two files at the marked boundary: everything from `# server.py` through `serve_forever()` belongs in `server.py`, and everything after `# client.py` belongs in `client.py`. The files use only Python modules from the standard library. In terminal A run the same interpreter command established in the Python course: `python3 server.py` on macOS or Linux, or `py server.py` on Windows. Expect “Serving on http://127.0.0.1:8000.” The address is loopback, so the teaching service accepts connections only from the same computer.
In terminal B run `python3 client.py` or `py client.py`. Expect exactly `201 CREATED` and then `409 ALREADY_ENROLLED`. Keep terminal A visible because it owns the running server process; terminal B owns a short-lived client process. Stop terminal A with Ctrl+C after the observation. The first lesson does not need Flask, Django, FastAPI, npm, curl, PostgreSQL, Docker, an account, or a deployment. Frameworks and production adapters can be valuable later, but they should not hide the first request boundary.
Read the request and response as two complete messages
The client expresses the intention “enroll learner 7 in course 42.” It sends an HTTP request with method `POST`, path `/courses/42/enrollments`, `Content-Type: application/json`, a byte length, and body `{"learnerId": 7}`. POST means the client asks the target collection to process a submitted representation. The path identifies course 42’s enrollment collection. The header tells the server how to interpret the body, and JSON represents one object as text. The two processes communicate through bytes; they do not share Python variables or call one another’s functions directly.
The server first chooses a route from method and path, then checks media type, size, JSON syntax, and learner identity. Only a valid message reaches the application decision. A first identity enters the in-memory enrollment set and returns status 201, JSON status `CREATED`, and a `Location` header naming the new resource. Repeating the identity returns 409 and `ALREADY_ENROLLED` because current application state conflicts with a second creation. A response contract includes status, headers, body shape, and meaning for both success and failure; client code should not guess success by searching human prose.
Separate transport evidence from durability and concurrency claims
The route and message checks form the transport boundary. The set membership check is the first application rule. The set itself is an in-memory teaching adapter: it lives only inside the server process and is cleared by restart. This baseline proves that one local client and server exchanged the documented messages in sequence. It does not prove durable storage, simultaneous-request correctness, authentication, authorization, encryption, multi-process coordination, capacity, deployment, backup, or recovery. Naming those limits is part of learning, not a defect in a deliberately small first experiment.
Restart the server and rerun the client. The first response becomes 201 again because the prior set no longer exists. Predict that result before running it and record it as evidence of volatility. Later, SQLite or another database adapter will persist facts and constraints across process restarts; integration and concurrency tests will then be required at that real boundary. A Web framework may provide routing and middleware, but the same contract ownership remains: untrusted message in, validated command, authorized application decision, durable change, and bounded response out.
Use one mutation and one fault to locate the changing owner
For the controlled mutation, change the client loop from `(7, 7)` to `(7, 8)`. Predict `201 CREATED` twice on a fresh server because the identities differ. Run it, record the two lines, restore `(7, 7)`, restart the server, and recover the original 201/409 baseline. Only application input changed; server address, HTTP method, path, JSON representation, routing, and status mapping stayed fixed. This makes the relationship between identity and conflict observable.
For the transport fault, change only the client URL from `/courses/42/enrollments` to `/course/42/enrollments`. Predict `404 NOT_FOUND` because no route owns the singular path. Observe it, restore the exact URL, and rerun 201/409. If a different error occurs, inspect the first visible message, verify which process is running, and change one cause at a time. Hand the two files and an evidence table to another learner, who should reproduce both experiments and explain what the baseline proves and does not prove.