Concepts You Should Know as a Mid-Level Backend Engineer
Becoming a mid-level backend engineer isn’t about learning more frameworks or racking up years of experience. It’s about moving from building features to owning systems — understanding how they work, making good design choices, planning for failure, and fixing things when they break.
There’s no single checklist that applies everywhere — this varies by company and team. Examples below use Go, PostgreSQL, Redis, and RabbitMQ, but the ideas apply broadly.
What a mid-level engineer can typically do
Section titled “What a mid-level engineer can typically do”- Take a feature from requirements to deployment and upkeep
- Design clear, maintainable APIs and data models
- Reason about concurrency, performance, and security — not just rely on defaults
- Connect to databases, caches, and queues, and handle failures
- Write solid tests and give useful code review feedback
- Debug production issues using logs, metrics, and traces
- Explain trade-offs and know when a simpler solution is better
You don’t need to master every distributed-systems algorithm or run a huge microservices platform. Good judgment matters more than a big tech stack.
1. Programming fundamentals
Section titled “1. Programming fundamentals”Know your language beyond syntax — how it actually runs.
- Memory: stack vs. heap, pointers, garbage collection
- Types: interfaces, generics, encapsulation
- Errors: how to propagate, wrap, and handle them
- Concurrency: goroutines, channels, mutexes, race conditions, deadlocks
- Resource cleanup: closing connections, graceful shutdown, avoiding leaks
- Tooling: linters, debuggers, profilers
For Go specifically: understand goroutines, channels, context.Context, and the difference between concurrency and parallelism. Know how a request’s context flows through to database calls and what happens when a request times out.
Try this: Build a concurrent worker pool with graceful shutdown and cancellation. Run the race detector.
2. Databases
Section titled “2. Databases”Databases aren’t just storage — schema design, queries, and transactions directly affect correctness and speed.
- Modeling: keys, relationships, constraints
- Normalization: why duplication causes problems, and when to break the rules
- Indexes: how they work, and that they aren’t free (they slow writes)
- Query plans: read
EXPLAIN ANALYZEoutput, spot slow scans - Transactions: ACID properties, commit/rollback
- Isolation: locking, lost updates, deadlocks
- Integration: connection pools, timeouts, migrations
Try this: Diagnose a slow endpoint by reading its query plan before touching code. Build a transaction that handles two simultaneous requests safely.
3. API design
Section titled “3. API design”An API is a contract — it should be predictable and able to change without breaking clients.
- HTTP methods, status codes, headers
- Clear, resource-oriented URLs
- Validation, pagination, filtering
- Idempotency, timeouts, retries, rate limits
- Versioning without breaking things
- When to use WebSockets, SSE, or gRPC instead of plain HTTP
Try this: Design an order-creation endpoint with an idempotency key. Explain what happens if the server finishes the order but the client’s connection drops first.
4. Software architecture
Section titled “4. Software architecture”Architecture manages complexity — patterns are tools, not requirements.
- Styles: layered architecture, modular monoliths, hexagonal architecture, microservices
- Principles: separation of concerns, SOLID, coupling vs. cohesion
- Patterns: repository, dependency injection, pub/sub
- CQRS and event sourcing — powerful, but add real complexity
Try this: Given a feature request, decide where module boundaries belong — and be ready to explain why a modular monolith might beat microservices here. Don’t reach for DDD or CQRS just to seem advanced; use them only when they solve a real problem.
5. Caching
Section titled “5. Caching”Caching speeds things up but creates a second source of truth, which adds risk.
- Strategies: cache-aside, write-through, write-behind
- Policy: TTLs, eviction, invalidation
- Risks: stale data, cache stampedes, outages
- Redis basics: data structures, expirations, atomic ops
Try this: Add caching to a read-heavy endpoint. Decide the TTL, invalidation strategy, and fallback if Redis goes down. Don’t add Redis just because it’s common — know what problem it’s solving first.
6. Async processing & messaging
Section titled “6. Async processing & messaging”Queues move work out of the request path but introduce new questions about ordering and duplicates.
- Models: work queues, pub/sub, competing consumers
- RabbitMQ basics: exchanges, queues, acknowledgments
- Delivery: at-least-once vs. at-most-once (exactly-once is hard)
- Failure handling: retries, backoff, dead-letter queues
- Consumer correctness: idempotent handlers, deduplication
Try this: Build a worker that only acknowledges messages after success, retries failures, and sends poison messages to a dead-letter queue. Understand the dual-write problem — a transactional outbox can keep a database update and an outgoing event consistent.
7. Security
Section titled “7. Security”Security is part of everyday engineering, not a pre-launch checklist.
- Identity: sessions, tokens, OAuth 2.0, OpenID Connect
- Access control: RBAC, least privilege, object-level checks
- Passwords: hash with Argon2id or bcrypt — never store plaintext
- Common risks: SQL injection, XSS, CSRF, SSRF, broken access control
- Transport: TLS, secure cookies, CORS
Try this: Review an endpoint that takes a resource ID — confirm the caller is authorized for that specific object, not just logged in. Remember: CORS is a browser rule, not real authentication, and a JWT is just a token format.
8. Testing
Section titled “8. Testing”Tests should build real confidence, not just raise a coverage number.
- Unit tests for isolated logic
- Integration tests against real dependencies
- End-to-end tests for critical flows
- Test doubles: know when mocks hide real problems
- Table-driven tests, race detection, fuzzing (in Go)
Try this: For a feature touching both PostgreSQL and RabbitMQ, write unit tests plus integration tests that check transaction behavior and message handling — including failure paths.
9. Distributed systems
Section titled “9. Distributed systems”Networked components fail independently. A timeout doesn’t tell you whether the operation actually succeeded.
- Failure modes: latency, partial failure, network partitions
- Resilience: retries with backoff and jitter, circuit breakers, graceful degradation
- Scaling: load balancing, replication, sharding
- Consistency: strong vs. eventual, the CAP theorem in practice
- Coordination: distributed locks, leader election
Try this: Design a service that calls another service. Decide on timeouts, which errors are safe to retry, and how to avoid retry storms. You don’t need to implement consensus algorithms yourself — just understand what problems they solve.
10. DevOps & infrastructure
Section titled “10. DevOps & infrastructure”You don’t need to replace a DevOps team, but you should understand how your app is built and run.
- Linux: processes, permissions, environment variables
- Networking: DNS, TCP, TLS, reverse proxies
- Containers: images, volumes, resource limits
- CI/CD: automated tests and builds
- Kubernetes: Deployments, Pods, Services, probes (useful, but not the first tool to reach for)
- Deployment strategies: rolling, blue-green, canary
Try this: Containerize an app with Docker Compose, add a CI pipeline, deploy to a test environment, and write down how to roll back a bad release.
11. Observability
Section titled “11. Observability”A service can return 200s and still be broken. Observability tells you what’s actually happening.
- Logs: structured, with correlation IDs, no sensitive data
- Metrics: error rates, latency, resource use
- Traces: follow a request across services
- Latency percentiles: p95/p99 matter more than averages
- SLIs/SLOs/SLAs and error budgets
- Postmortems: blameless, focused on learning
Try this: Instrument an API. When latency spikes, use traces to find the bottleneck, metrics to scope the problem, and logs to find the cause.
12. Engineering practices
Section titled “12. Engineering practices”Being mid-level also means helping the team and codebase stay healthy.
- Git branching, rebasing, resolving conflicts
- Reviewing code for correctness, clarity, and risk — not just style
- Naming, refactoring, reducing complexity
- Managing technical debt deliberately
- Writing lightweight decision records for big choices
Try this: Own a feature end-to-end — tests, docs, migration review, backward compatibility, observability, and a clear write-up of trade-offs.
How to prioritize
Section titled “How to prioritize”| Priority | Topics | Depth needed |
|---|---|---|
| Core | Language fundamentals, databases, APIs, architecture, testing, security | Should be able to implement, explain, and debug independently |
| Strong working knowledge | Caching, messaging, DevOps basics, observability | Should understand failure modes and make sound choices |
| Progressive depth | Advanced distributed systems, large-scale DB architecture, Kubernetes, reliability engineering | Understand the basics; go deeper as your role demands |
Suggested learning order (for Go):
- Go fundamentals, concurrency, testing
- PostgreSQL modeling, indexes, transactions
- API design, auth
- Architecture and integration testing
- Redis, RabbitMQ, idempotency
- Linux, Docker, CI/CD, observability
- Distributed systems (start simple, go deeper over time)
This is a suggestion, not a strict order — keep building projects as you go.
A capstone project
Section titled “A capstone project”Build an order management API that touches most of these concepts without needing a huge microservices setup:
- Auth with roles
- Inventory with relational constraints
- Transactional, idempotent order creation
- Safe handling of concurrent stock requests
- Redis caching on read-heavy endpoints
- RabbitMQ for async notifications, with retries and dead-letter handling
- Real integration tests
- Docker Compose + CI
- Logs, metrics, and traces
- Load tests and a backup/recovery plan
Start as a modular monolith. Only split into separate services when you have a concrete business or scaling reason to.
The bottom line
Section titled “The bottom line”Being mid-level isn’t about memorizing tools — it’s about judgment: building software that works correctly, stays understandable, and can be operated and changed safely. Go deep on fundamentals, learn tools by solving real problems, and treat every production issue as a chance to get better.