Talently
Talently
Back-End Developer

Back-End Developer

Builds the systems, APIs, and business logic that power digital products at scale.

A Back-End Developer designs and implements the server-side logic, APIs, and data layer that underpin digital products. They are responsible for ensuring that systems are secure, scalable, and maintainable. They work closely with front-end developers, architects, DBAs, and infrastructure teams to define API contracts, model data, and resolve performance bottlenecks under real production load. Their work largely determines the reliability and long-term evolvability of the product.

PythonREST APIs / GraphQLSQL / NoSQLDockerAuthentication & SecurityJava

Recruit the best Back-End Developer here

Start now

Main Responsibilities

  • Design and implement REST or GraphQL APIs with well-defined contracts, versioning, and documentation (OpenAPI, GraphQL schema).
  • Model and optimize relational and non-relational database schemas based on the required access patterns.
  • Implement secure authentication and authorization mechanisms (OAuth2, JWT, RBAC).
  • Design asynchronous workflows using message queues (Kafka, RabbitMQ, SQS) to decouple services and absorb traffic spikes.
  • Diagnose and resolve performance bottlenecks in queries, caching layers, and resource consumption.
  • Write automated tests (unit, integration, contract) and maintain reliable CI/CD pipelines.

Key Skills

Technical Skills

  • Proficiency in at least one back-end language — Node.js, Python, Java, Go, or .NET — with its primary frameworks
  • REST and GraphQL API design: resources, contracts, versioning, pagination, and error handling
  • Relational databases (PostgreSQL, MySQL) and NoSQL (MongoDB, Redis, DynamoDB) with sound judgment on when to use each
  • Authentication and authorization implementation with OAuth2, JWT, and permission models (RBAC, ABAC)
  • Messaging and async queue systems: Kafka, RabbitMQ, SQS, or equivalent
  • Containerization with Docker and basic orchestration with Kubernetes or equivalent cloud-managed services
  • Testing: unit tests with dependency mocking, integration tests against real databases, contract testing with Pact

Soft Skills

  • Ability to design technical solutions and communicate them clearly — via diagrams or written documentation — to different audiences
  • Sound judgment for evaluating trade-offs between consistency, availability, and performance in architecture decisions
  • Active collaboration with front-end teams to define API contracts before implementation begins
  • Systems thinking to trace the downstream impact of schema or API changes on dependent services
  • Proactiveness in identifying technical debt and proposing refactors with a clear business justification
  • Assertive communication when pushing back on technically infeasible requirements with non-technical stakeholders

Real use cases

Context

Back-end APIs are the contract between the server and its consumers — web, mobile, and third parties. Poor API design leads to tight coupling, breaking changes, and accumulated technical debt.

Real examples

  • RESTful APIs with URL or header-based versioning for non-breaking evolution
  • GraphQL APIs with schema federation for multi-team environments
  • APIs with rate limiting, throttling, and per-client quotas
  • Automatic documentation with OpenAPI/Swagger integrated into the CI pipeline

Context

Not every operation can or should run synchronously. Message queues and event systems allow services to be decoupled, absorb load spikes, and guarantee eventual processing.

Real examples

  • Payment processing with exactly-once delivery guarantees (idempotency)
  • Email and push notification delivery decoupled from the main request flow
  • Data ingestion pipelines with Kafka supporting multiple downstream consumers
  • Background job workflows with retry logic and dead-letter queues

Context

Back-end security is non-negotiable. A poorly designed authentication model is the root cause of the majority of critical vulnerabilities in web applications.

Real examples

  • OAuth2 Authorization Code with PKCE flow for SPA and mobile clients
  • Granular permission systems based on roles (RBAC) or attributes (ABAC)
  • Multi-tenant authentication with per-organization data isolation
  • Automatic token rotation and secure session management

Context

A system that performs well in development can break under real production load. The back end must identify and resolve bottlenecks before they impact users.

Real examples

  • SQL query optimization using EXPLAIN plans and composite indexes
  • Layered caching with Redis (query cache, session cache)
  • Efficient cursor-based pagination for large result sets
  • N+1 query detection using APM tools such as Datadog or New Relic

Context

Database schemas evolve alongside the product. Managing migrations without downtime is a critical skill for systems with active users in production.

Real examples

  • Zero-downtime migrations using the expand-contract pattern
  • Migration versioning with Flyway, Liquibase, or Alembic
  • Safe data backfills on tables with millions of rows
  • Rollback strategies for failed migrations in production environments

Basic questions

Relational when data has a well-defined structure, complex relationships, or requires transactional consistency (ACID). NoSQL when data is semi-structured, the schema evolves frequently, you need massive horizontal scale, or access patterns are simple and predictable. The team's familiarity and the existing ecosystem are also real factors in that decision.
Use nouns for resources (not verbs), hierarchies that reflect real relationships, semantically correct HTTP methods (idempotent GET, POST to create, PUT/PATCH to update), appropriate status codes, and structured error responses. Version from day one if the API is public or shared across teams.
Sessions: centralized server-side state, immediate revocation, better suited for traditional web apps on a single domain. JWT: stateless, scales horizontally without coordination, well-suited for APIs consumed by SPAs and mobile clients. JWT's main drawback is revocation — if a token is compromised before it expires, there is no native way to invalidate it without maintaining a server-side blocklist.
An operation is idempotent if executing it multiple times produces the same result as executing it once. GET, PUT, and DELETE should be idempotent by definition. For critical operations like payments or order submissions, designing POST endpoints with idempotency keys prevents duplicate processing when clients retry after a network failure.
Define a consistent error format with a machine-readable code, a human-readable message, and an optional details field for the client. Clearly separate client errors (4xx) from server errors (5xx). Never expose stack traces in production. Log errors with enough context (request ID, user ID, timestamp) for debugging without leaking sensitive data.
Idempotency keys from the client for critical operations. Database transactions with uniqueness constraints. The outbox pattern to guarantee that an event is emitted exactly once, atomically with the database write. In messaging systems, idempotent consumers that can safely reprocess the same message without additional side effects.
Authentication: verifying that the user is who they claim to be (JWT, session, API key). Authorization: verifying that the authenticated user has permission to perform the requested action. These are implemented in layers — an authentication middleware runs first to validate the token, followed by per-endpoint or per-resource authorization guards that evaluate roles or policies.
Avoid offset-based pagination at scale — it's inefficient because the database must count and skip rows. Prefer cursor-based pagination: the client sends the ID or timestamp of the last item received, and the query uses WHERE id > cursor LIMIT n. This is O(log n) with an index rather than O(n), and handles concurrent inserts correctly.

Technical questions

In a multi-tenant context, beyond verifying identity you must also resolve which tenant the user belongs to and what they are allowed to do within that tenant. This means embedding the tenant_id in the token, isolating data at the query level (row-level security in PostgreSQL, or mandatory ORM filters), and deciding whether permissions are global or tenant-scoped.
N+1 occurs when a query is executed for each item in a list rather than a single query with a JOIN or batch fetch. Prevention: eager loading configured at the ORM level, DataLoader for GraphQL, or explicit queries with JOINs. Diagnosis: slow query logging, APM tooling, or manual instrumentation that counts queries per request.
Implement circuit breakers to prevent cascading failures. Define explicit per-service timeout values. Differentiate recoverable errors (retries with exponential backoff) from permanent failures. Use the saga pattern or compensating transactions for distributed operations that require rollback. Log with correlation IDs to trace errors across service boundaries.
ORM: higher developer productivity, built-in SQL injection protection, portability across database engines. The downsides: engine-specific optimizations are off the table, it's easy to generate inefficient queries without realizing it, and debugging requires understanding what SQL the ORM is actually producing. For high-traffic workloads with complex queries, a pragmatic approach is to use the ORM for straightforward CRUD and drop to raw SQL for performance-critical queries.
Choose a caching strategy based on the access pattern: cache-aside (the application manages the cache), write-through (writes go to cache and database simultaneously), or read-through. Invalidation is the hard part: options are fixed TTL, event-driven invalidation triggered when the underlying data changes, or version-based cache busting. Avoid caching data that changes frequently or has high key cardinality.
Within a single database: ACID transactions with BEGIN/COMMIT. Across services: the saga pattern with orchestration or choreography, where each step emits an event and failures trigger compensating transactions. The outbox pattern ensures the event and the database write happen atomically, eliminating the risk of writing to the database but failing to emit the event.
Validate file type by magic bytes, not by file extension. Enforce a size limit. Never store files using the original filename provided by the user. Process files in an isolated process or sandbox. If files are served publicly, do so from a separate domain to prevent XSS. Scan with antivirus where the context requires it. Never execute uploaded files.
URL versioning (/v1/, /v2/) is the most explicit and easiest to route. Header-based versioning (Accept: application/vnd.api+json;version=2) is more RESTful but harder to implement and document consistently. Adopt a clear deprecation policy: announce changes well in advance, maintain old versions for a defined support window, and provide migration guides. Never introduce breaking changes within an existing version.

Advanced questions

Start by profiling to identify the actual bottleneck: is it CPU, database I/O, memory, or network? Scale horizontally behind a load balancer if the bottleneck is CPU-bound. Add read replicas if the load is read-heavy database traffic. Implement aggressive caching for frequently accessed queries. Evaluate whether synchronous operations can be moved to async queues. As a last resort, revisit the data model or the architecture of the most expensive query.
The most common mistake is decomposing too early. Identify bounded contexts with high internal cohesion and low inter-context coupling. Extract the services that benefit most from independent scaling or independent deployment first. The primary cost of microservices is distributed communication: latency, eventual consistency, and observability complexity. Build out your observability infrastructure — distributed tracing, centralized logging — before you start breaking things apart.
Use a centralized identity provider (Auth0, Keycloak, or a custom solution) that issues tokens carrying tenant and role claims. Implement OIDC for SSO. Granular permissions (ABAC) are evaluated at each service using the token claims combined with the context of the requested resource. Cache authorization decisions with a short TTL. Audit all access to sensitive resources.
The three pillars: structured logs (JSON) with a correlation ID that flows through every service, metrics with p95/p99 latency percentiles and error rates, and distributed tracing with OpenTelemetry. For intermittent failures, metrics alert you that something is wrong, tracing shows you which service and operation is failing, and logs give you the specific error context. Without all three, production debugging is essentially guesswork.
Encryption in transit (TLS 1.3) and at rest for sensitive fields. Least-privilege credentials for database access. Structured logging that never includes PII. Input sanitization to prevent SQL injection, XSS, and path traversal. Rate limiting and abuse detection. Automated secret rotation via a secrets manager (Vault, AWS Secrets Manager). Periodic threat modeling and penetration testing on architecture changes.
Use a durable queue (Redis with AOF persistence, SQS, or Kafka) rather than in-memory queues. Implement the outbox pattern to atomically enqueue jobs alongside database writes. Design idempotent workers that can safely reprocess the same message without additional side effects. Define a dead-letter queue for jobs that fail repeatedly. Monitor queue lag as a key system health metric.

Common interview mistakes

Saying 'I used microservices' or 'I added caching' without explaining what problem it solved, what alternatives you considered, and what trade-offs you accepted signals recipe-following, not engineering judgment. Senior-level interviewers will always ask 'why that solution and not X?' — you need to be ready for that question.
Any API design, authentication flow, or data processing system that doesn't address authentication, authorization, input validation, or secrets management is a red flag. In back-end roles, security is part of the design — not a separate step you add at the end.
Recommending Kafka, Kubernetes, and database sharding for an application with 1,000 daily active users signals over-engineering. Interviewers assess whether you can calibrate solution complexity to the actual scale of the problem — that judgment is itself a core engineering skill.
Many developers use ORMs without understanding the underlying SQL. In back-end interviews focused on performance, being unable to explain what query runs behind a frequent operation — or never having run EXPLAIN ANALYZE — is a significant gap that experienced interviewers will notice.
Candidates often describe distributed systems without a clear understanding of the consistency guarantees they actually provide. Saying a system is 'eventually consistent' without explaining which inconsistency scenarios are acceptable and which are not raises real doubts about the depth of your understanding.
A system that works in development but lacks structured logging, metrics, health checks, or a deployment strategy is an incomplete design. At companies running systems in production, observability and deployability are considered part of the design — not an afterthought you deal with later.