Team Lead / Technical Lead — Interview Questions
In short: A tech lead owns technical direction for a team: architecture and design decisions, code quality, breaking down work, unblocking developers and mentoring — and still writes significant code.
- An engineering manager owns people and delivery: hiring, career growth, performance reviews, team health, process and cross-team coordination, with less hands-on coding.
- In smaller teams (and in many Indian IT services companies' "team lead" roles) one person does parts of both.
In short: Accountabilities: Product Owner (maximises value, owns and orders the Product Backlog), Scrum Master (coaches the team and organisation in Scrum, removes impediments), Developers (create the increment, own the Sprint Backlog).
- Events: the Sprint (container), Sprint Planning, Daily Scrum (15 min), Sprint Review, Sprint Retrospective.
- Artifacts with commitments: Product Backlog → Product Goal, Sprint Backlog → Sprint Goal, Increment → Definition of Done (2020 Scrum Guide).
In short: Waterfall is sequential — requirements, design, build, test, deploy — each phase completed before the next, with change being costly;
- it suits fixed, well-understood requirements and regulated contexts.
- Agile delivers in short iterations with working software each cycle, continuous feedback, and embraces change; it suits evolving requirements.
- Many enterprises use hybrids (fixed milestones with agile execution).
In short: Clarify scope and acceptance criteria;
- break it into small tasks (≤ 2–3 days) including testing, reviews, deployment and documentation;
- estimate with the people doing the work (planning poker or three-point estimates);
- add buffers for risks and unknowns (spikes for unclear areas);
- account for real capacity (meetings, support, leaves);
- state assumptions and dependencies explicitly;
- give a range rather than a single date;
- re-forecast as you learn and communicate changes early.
In short: I delegate outcomes with context: why it matters, what done looks like, constraints, deadline and check-in points.
- I match tasks to skills and growth goals — a stretch task for a junior with a senior pairing, critical-path work for experienced people — and adjust my involvement to their experience (directing for new members, delegating to experts).
- Work is tracked on the board, I stay available to unblock, and I avoid taking the hardest tasks myself all the time so the team grows and I don't become a bottleneck.
In short: Regular 1:1s and pairing sessions;
- clear coding standards and examples;
- code reviews that explain the why rather than just fixing things;
- gradually larger tasks with support (first a bug fix, then a small feature, then owning a module);
- encouraging questions and making it safe to make mistakes;
- sharing resources and certification paths;
- and celebrating progress publicly.
- I measure success by how independently they can deliver after a few months.
In short: Gather facts first, then talk privately and early with specific examples, and listen to understand the cause (unclear expectations, skill gap, personal issues, motivation, wrong role).
- Agree on a concrete improvement plan with support — training, pairing, smaller scoped tasks — and regular check-ins.
- Recognise improvements.
- If there's no progress after a fair period, involve my manager and HR for a formal plan, documenting everything and treating the person with respect throughout.
In short: Speak with each privately to understand their perspective and underlying concerns, then bring them together focused on facts and the shared goal.
- For technical disagreements, agree criteria and use data or a quick spike to decide;
- if still split, I make the call, explain the reasoning, record it (ADR) and ask both to commit.
- For personal friction, set expectations for respectful behaviour and follow up.
- I address conflict early, before it affects the team.
In short: Protect the Sprint Goal.
- I acknowledge the request, assess urgency and impact with the Product Owner: if it's truly critical (production issue, regulatory), we swap out work of similar size and make the trade-off visible;
- otherwise it goes into the backlog for the next sprint planning.
- For client projects, significant changes go through impact analysis and a change request covering effort, timeline and cost.
In short: Regular, concise updates: progress vs plan (with a RAG status), key achievements, risks and issues with mitigation plans, decisions needed from them, and next steps.
- I share bad news early with options and a recommendation, back statements with data (burnup charts, defect counts), tailor the detail to the audience (executives vs product owners), and keep a written record.
In short: Start from requirements (including non-functional ones and constraints like budget, skills and timelines), list options, evaluate them against agreed criteria, run a spike or proof of concept for risky unknowns, involve the team and relevant experts, decide (and own the decision), and document it in an Architecture Decision Record with context, alternatives and consequences.
- Revisit decisions when context changes.
- I prefer reversible decisions made quickly and irreversible ones made carefully.
In short: At team level, never individuals by raw output: DORA metrics (deployment frequency, lead time, change failure rate, time to restore), flow metrics (cycle time, throughput, WIP), quality (escaped defects, incident trends), predictability (planned vs delivered), and team health (engagement, attrition, on-call load).
- Business outcomes — features adopted, customer issues resolved — matter most.
- I use metrics to find improvement opportunities, not to rank people.
In short: Verify the estimate with the team and find the cause.
- Inform stakeholders immediately — no surprises — with options: reduce scope (ship must-haves first, defer nice-to-haves), add focused help where it actually speeds things up (not late-stage new joiners on complex code — Brooks's law), remove distractions from the team, parallelise testing, or move the date.
- Give a recommendation, get a decision in writing, update the plan, and run a retrospective later on why the risk wasn't spotted earlier.
In short: Acknowledge and assess severity;
- assemble the right people (on-call, owners) and appoint an incident lead;
- open a communication channel and inform stakeholders with regular updates.
- Prioritise restoring service — roll back the last deployment, toggle a feature flag, fail over, scale up — before deep root-cause analysis.
- Once stable, monitor, then do a blameless post-mortem the next week with a timeline, root causes (5 Whys), and owned action items (tests, alerts, runbooks, safer deployments).
- Make sure people who worked late get time off.
In short: Make the impact visible: maintain a prioritised backlog with estimates, show the client what each new request displaces, and agree priorities together (MoSCoW: Must/Should/Could/Won't).
- Use a change-request process for contractual scope, protect the current sprint, and propose phased delivery — an MVP by the date, the rest in later releases.
- Document decisions and keep the relationship collaborative rather than confrontational.
In short: Have an honest conversation to understand the reasons — if it's fixable (role, growth, compensation) and appropriate, explore a counter-offer with my manager, but don't rely on it.
- Immediately plan the knowledge transfer: document their modules, pair them with others during the notice period, record walkthroughs, and review critical code together.
- Re-plan the release with stakeholders based on reduced capacity.
- Longer term, reduce single points of failure by rotating ownership, and look at retention across the team.
In short: HLD shows the architecture of the whole system: components (clients, load balancers, services, databases, caches, queues, CDN), how they interact, data flow, scaling and failure handling.
- LLD details the inside of components: classes, interfaces, methods, design patterns, database tables and APIs.
- HLD answers "what pieces and how they connect"; LLD answers "how each piece is built".
In short: Vertical: give one machine more CPU/RAM — simple, no code change, but limited and a single point of failure.
- Horizontal: add more machines behind a load balancer — almost unlimited and fault tolerant, but services must be stateless and data must be replicated/partitioned.
- Typical path: scale up first, then out.
In short: Storing frequently read data in faster storage (memory/Redis/CDN) to reduce latency and database load.
- Cache-aside: on read, check the cache;
- on miss, read from the database and put the result in the cache with a TTL;
- on write, update the database and delete (or update) the cache key.
- Watch for stale data, stampedes and hot keys.
In short: Distributes requests across multiple servers to increase capacity and availability, performs health checks to stop sending traffic to failed servers, and can terminate TLS and route by URL (L7).
- Algorithms: round robin, least connections, IP hash/consistent hashing, weighted.
In short: In a distributed system, during a network partition you can't have both strong consistency and full availability.
- CP systems reject or delay requests to avoid stale data (bank balances, configuration stores like etcd);
- AP systems keep answering and reconcile later (social feeds, DNS, carts in Dynamo-style stores).
- When there is no partition, the trade-off is latency vs consistency (PACELC).
In short: Start from the data and access patterns.
- SQL for structured, related data needing transactions, joins and constraints (orders, payments).
- NoSQL when you need massive horizontal scale for simple key-based access, flexible schemas, very high write throughput, or special models (documents, graphs, time-series).
- Many systems combine them: SQL as the source of truth, Redis cache, search index, blob storage.
In short: Splitting a dataset horizontally across multiple database nodes so each stores a subset, increasing capacity and throughput.
- A good shard key has high cardinality, spreads reads and writes evenly (no hot shard) and matches the main query pattern so most queries hit one shard (e.g. customer id for orders).
- Use consistent hashing or a directory to allow adding shards with minimal data movement.
In short: Nodes and keys are placed on a hash ring; each key belongs to the next node clockwise.
- When a node is added or removed, only the keys between it and its neighbour move (~1/N of keys), unlike
hash % Nwhere almost all keys move. - Virtual nodes (many points per server) spread load evenly.
- Used in distributed caches and databases (Dynamo, Cassandra) and load balancers.
In short: To decouple services (the producer doesn't wait for or depend on the consumer being up), absorb traffic spikes (load levelling), process work asynchronously (emails, video encoding), retry failures with dead-letter queues, and fan out events to multiple subscribers.
- Consumers must be idempotent because delivery is at-least-once.
In short: Write API creates a short code (base62 of a unique id or random 7 chars with collision check) and stores code → long URL in a key-value store or SQL with a unique index;
- read path
GET /{code}checks Redis, then the store, and returns a 301/302 redirect. - Reads dominate (100:1) → cache and CDN/edge.
- Analytics: send click events to a queue/stream and aggregate asynchronously.
- Scale: shard by code; custom aliases, expiry and abuse checks as extras.
In short: Fan-out on write pushes post ids into followers' precomputed feeds (fast reads, expensive for celebrities);
- fan-out on read builds the feed at read time (cheap writes, slow reads).
- Use a hybrid: push for normal users, pull and merge for celebrity accounts.
- Store feeds in Redis lists, posts in a sharded store, media via CDN, use cursor pagination, and rank asynchronously.
In short: Measure the top queries first;
- add/fix indexes and remove N+1 queries;
- add caching (in-memory/Redis) for hot data and HTTP caching/CDN where possible;
- route reads to read replicas;
- reduce payloads with projection and paging;
- scale the database up as a quick fix;
- finally partition/shard if data or write volume demands it.
- Monitor after each step.
In short: Load test beforehand;
- pre-scale and warm caches;
- serve static content from the CDN;
- put a virtual waiting room/queue in front of checkout;
- rate limit per user;
- make inventory reservation atomic (conditional update) to prevent overselling;
- move non-critical work (emails, analytics) to queues;
- use feature flags to switch off expensive features;
- monitor dashboards and have a rollback plan.
Clarify requirements and scope → list use cases → find entities (nouns) and behaviours (verbs) → define relationships (composition, inheritance, interfaces) → draw a class diagram → apply SOLID and suitable patterns (Strategy, State, Factory, Observer) → write code for the main flows → discuss edge cases, concurrency, extensibility, testing and persistence.
In short: Prefer composition ("has-a") because it keeps classes small and flexible: behaviour can be swapped at runtime via interfaces (Strategy) and changes don't ripple through a hierarchy.
- Use inheritance only for genuine "is-a" relationships with shared behaviour and substitutability (Liskov).
- Deep inheritance trees become rigid and fragile.
In short: Association: objects know/use each other (Teacher ↔ Student).
- Aggregation: whole–part where the part can exist independently (Team has Players; players exist without the team) — hollow diamond.
- Composition: strong ownership where the part's lifetime depends on the whole (House has Rooms; Order has OrderLines) — filled diamond.
In short: ParkingLot (facade/singleton via DI) composed of Floors composed of Spots;
- Vehicle hierarchy (Car, Bike, Truck) or a type enum;
- Ticket;
- FeeCalculator with Strategy per vehicle type;
- optional Observer for display boards;
- Factory for vehicles.
- Concurrency: lock or atomic spot allocation so two gates can't take one spot.
- Extensibility: new vehicle/spot types and pricing rules without modifying existing classes.
In short: When an object's behaviour changes with its internal state and you'd otherwise have large switch statements on state.
- Each state is a class implementing the same interface; the context delegates to the current state, and states trigger transitions.
- Examples: vending machine (Idle, HasMoney, Dispensing, SoldOut), order lifecycle, ATM, elevator, document approval.
In short: Identify shared mutable state and make the critical operation atomic: locks around allocation in a single process, atomic compare-and-set (Interlocked, ConcurrentDictionary.TryAdd), optimistic concurrency (row version) or unique constraints in the database, distributed locks with TTL (Redis SET NX PX) across servers.
- Seat booking: temporary locks with expiry, confirm on payment, unique (ShowId, SeatId) constraint as the final guarantee.
In short: Chain of Responsibility: handlers for ₹2000, ₹500, ₹200 and ₹100 notes;
- each dispenses as many notes as it can (
amount / denomination, limited by its stock) and passes the remainder to the next handler; - if a remainder is left at the end, the request fails and nothing is dispensed (check first, then commit).
- Around it: ATM with the State pattern (Idle → CardInserted → Authenticated → Transaction), a BankService interface for balance/debit, and transaction logging.
In short: A hash map from key to a node in a doubly linked list ordered by recency.
- Get: find the node via the map, move it to the front, return the value.
- Put: if present, update and move to front; otherwise insert at the front, and if capacity is exceeded remove the tail node and its map entry.
- Add a lock for thread safety.
- In C#:
Dictionary<K, LinkedListNode<(K,V)>>+LinkedList<(K,V)>.
In short: 10 min: requirements and class diagram (User, Group, Expense, Split strategies, Ledger/BalanceSheet).
- 50 min: implement core — add expense with equal/exact/percent split (Strategy), update pairwise balances, show balances per user, validation (percentages sum to 100, exact amounts sum to total).
- 20 min: simplify debts (greedy matching of max creditor and max debtor), simple console/driver and a few unit tests.
- 10 min: refactor names, handle rounding, explain extensions (currencies, persistence, notifications).
In short: Presentation (UI), business logic (services/BLL) and data access (DAL/repositories), each calling only the layer below.
- It separates concerns and lets teams work on layers independently.
- Layers are logical; tiers are physical machines.
- Weakness: business logic often depends directly on data access, making it hard to test and change.
In short: An architecture that places the domain (entities, business rules) and application use cases at the centre and frameworks, UI and databases at the outside.
- The dependency rule: source-code dependencies only point inward — the domain depends on nothing;
- the application defines interfaces that infrastructure implements;
- the web project is the composition root that wires everything with DI.