Background Discussion: API Design, Async, Python Backend, Large Codebases
This document summarises what to emphasise when discussing your experience in these four areas during the interview.
1. API design and integration
What to highlight:
- Design: Resource-oriented URLs, consistent naming (e.g. plural nouns, one convention), correct HTTP methods and status codes (200, 201, 204, 4xx, 5xx), versioning (e.g.
/v1/), and clear request/response shapes. Use Pydantic (or similar) for validation and docs. - Integration with external APIs: Dedicated clients/adapters per source: one place for URL, auth, and serialisation. Normalise external responses into internal models so the rest of the app doesn’t depend on each API’s schema. Handle timeouts, retries (with backoff), and circuit breaker in the client layer.
- Errors: Don’t leak third-party errors; map to your own error types and status codes. Support partial failure (e.g. 200 with a body that indicates which sources failed) when you aggregate several APIs.
- Auth: API keys or tokens in headers, loaded from config/env; never in code or query params. Rotate and use different keys per environment.
Example experience to mention: “I designed/consumed REST APIs with clear contracts, and integrated multiple external providers behind a single service, with normalised models and centralised retry and error handling.”
2. Asynchronous workflows
What to highlight:
- When to use async: I/O-bound work (many HTTP calls, DB, cache) where you want concurrency in one process. Use async libraries (
httpx,aiohttp,asyncpg) and await so the event loop can run other tasks while waiting. - Concurrency: Use
asyncio.gatherto run several independent calls in parallel (e.g. three APIs at once). Use semaphores or similar if you need to cap concurrency (rate limits, connection limits). - What to avoid: Don’t call blocking code (sync HTTP,
time.sleep) inside async handlers; use executors or move that work to background workers. Async is for I/O concurrency, not for long CPU-bound or durable jobs. - When to use workers: For long-running, durable, or scheduled tasks (e.g. Celery), use a queue and workers. Use async for “many short I/O calls in this request/process.”
Example experience to mention: “I used asyncio and gather to call several external APIs in parallel, with timeouts and error handling, and moved long or scheduled jobs to a task queue.”
3. Python backend development
What to highlight:
- Stack: e.g. FastAPI (or Django/Flask) with async support, Pydantic for validation, dependency injection (
Depends) for services and clients. Use an ASGI server (e.g. Uvicorn) for async. - Layers: Clear separation: routers (HTTP only), services (business logic, orchestration), clients/repositories (external APIs, DB). Routers call services; services use injected clients; no HTTP or external schema inside services.
- Config and secrets: Environment variables or a secret manager; validate at startup (e.g. pydantic-settings). No secrets in code or repo.
- Observability: Structured logging (with request/correlation IDs), metrics (latency, errors per dependency), and clear error responses so you can debug and alert.
Example experience to mention: “I built/maintained Python backends with FastAPI, clear layering (routers, services, clients), async where it made sense, and env-based config and logging.”
4. Large codebases
What to highlight:
- Onboarding: Start from entry points and one user flow; follow request → router → service → client. Use tests and docs to see how pieces are used. Ask the team for “pain points” and conventions.
- Structure: Bounded contexts or modules (e.g. by feature or domain). Shared models and utilities in a clear place; avoid circular imports and “god” modules. Consistent naming and patterns so new code goes in the right place.
- Changing safely: Small, focused changes; run tests before and after. Match existing style and patterns; add tests for new behaviour. Use code review and avoid large, untested refactors early on.
- Dependencies: Prefer injection and interfaces (protocols) so you can test and swap implementations. In a large codebase, explicit dependencies and one place for cross-cutting behaviour (retries, auth, logging) keep things manageable.
Example experience to mention: “I’ve joined and contributed to large Python backends by following one flow at a time, respecting existing structure and tests, and making small, well-tested changes with clear dependencies.”
Summary for the interview
- API design and integration: Clear contracts, internal models, clients per source, retries/circuit breaker, and consistent error handling.
- Asynchronous workflows: Async for I/O concurrency (e.g.
gather), workers for long/durable work; avoid blocking the event loop. - Python backend: FastAPI (or similar), routers/services/clients, Pydantic, env/config, and observability.
- Large codebases: Understand one flow first, follow structure and tests, change in small steps, and keep dependencies explicit.
Use these as talking points when the interviewer asks about your background in these areas.
Interview angle
- “Tell me about your API experience.” - the answer that lands is one concrete system with a decision in it: why you normalised provider responses into internal models, where you put retries, what you did about partial failure. A feature list without a decision reads as a CV recital.
- “How do you approach a large unfamiliar codebase?” - trace one request end to end first, use the tests as documentation, then change something small and ship it. Saying “I read the whole thing” is the wrong answer for anything above 50k lines.
- “Where do you use async in production?” - I/O concurrency inside a request (fanning out to several providers with
gather), and a worker or queue for anything long-running or durable. Be ready for the follow-up on what happens when one call blocks the loop. - “What is the trap in these background questions?” - they sound like small talk and are actually scoping the rest of the interview. Whatever you claim depth in here is where the hard follow-ups will come from, so claim what you can defend.