Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Explaining Projects in Interviews

How to effectively discuss your projects during technical interviews. The goal is to demonstrate depth of understanding, engineering judgment, and the ability to make trade-offs.


The STAR Format for Projects

Adapt the STAR method (Situation, Task, Action, Result) for project discussions:

S — Situation: What was the context?

  • What problem were you solving?
  • Why did this project exist?
  • What were the constraints (time, resources, technology)?

T — Task: What was your specific role?

  • What were you responsible for?
  • What were the requirements?
  • What was the scope?

A — Action: What did you build and why?

  • Architecture decisions and trade-offs
  • Technologies chosen and why
  • Challenges encountered and how you solved them
  • Iterations and improvements

R — Result: What was the outcome?

  • Performance metrics
  • What you learned
  • What you’d do differently
  • Impact (users, performance, reliability)

The Architecture Discussion

When an interviewer asks “Tell me about a project you built,” structure your answer:

1. The Problem (30 seconds)

“I built a URL shortener because I wanted to understand how services like bit.ly handle millions of redirects with low latency. The key challenge was generating unique short codes efficiently and handling high read traffic.”

2. The Architecture (1-2 minutes)

“The system has three main components:

  • An API server in Go that handles short URL creation and redirects
  • PostgreSQL for persistent storage of URL mappings
  • Redis as a cache layer for hot URLs

The flow is: client sends a long URL → API generates a base62 code → stores in PostgreSQL → caches in Redis → returns the short URL. For redirects: client hits short URL → check Redis cache → if miss, query PostgreSQL → cache the result → return 301 redirect.“

3. Key Decisions and Trade-offs (2-3 minutes)

This is where you demonstrate depth:

Why base62 encoding? “I chose base62 over UUID because it produces shorter, human-readable URLs. I used a counter-based approach with encoding rather than random generation to avoid collisions, but I added a check-and-retry for safety.”

Why Redis for caching? “URL shorteners are read-heavy — typically 100:1 read-to-write ratio. Redis gives sub-millisecond lookups, which matters for redirect latency. I set TTLs based on URL activity — popular URLs stay cached longer.”

Why PostgreSQL over a NoSQL store? “I needed strong consistency for URL creation (no duplicates) and PostgreSQL’s indexing is excellent for lookups by short code. The relational model also made analytics queries straightforward.”

4. Challenges and Solutions (1-2 minutes)

“The biggest challenge was handling concurrent requests for the same long URL. Two requests could generate different short codes for the same URL. I solved this with a database unique constraint on the original URL and an upsert pattern — if the URL already exists, return the existing short code.”

“Another challenge was the hot key problem — if a URL goes viral, all requests hit the same Redis key. I handled this by adding local in-memory caching with a short TTL for the most popular URLs.”

5. Results and Learnings (30 seconds)

“The service handles 5,000 requests per second on a single instance with p99 latency under 5ms for redirects. I learned a lot about cache invalidation strategies and the importance of designing for the read path first in read-heavy systems.”


Common Interview Questions About Projects

“Why did you choose this technology?”

Bad answer: “Because it’s popular” or “Because I know it.”

Good answer: Connect the technology to the problem requirements.

“I chose Go because the service needed to handle many concurrent connections with low memory overhead. Go’s goroutines are lightweight compared to OS threads, and the standard library has excellent HTTP support. I considered Node.js but Go’s performance characteristics were better for this use case.”

Framework for answering:

  1. What were the requirements? (performance, team familiarity, ecosystem)
  2. What alternatives did you consider?
  3. Why did you choose this one?
  4. What trade-offs did you accept?

“What would you do differently?”

This question tests self-awareness and growth. Never say “nothing.”

Good answer: Show you’ve reflected on the project and learned from it.

“If I started over, I would:

  1. Add observability from day one — I added logging and metrics after the fact, and it was painful to retrofit. Next time, I’d instrument from the start.
  2. Use a schema migration tool — I managed database schemas manually, which caused issues when multiple developers were working on the project.
  3. Design the API more carefully upfront — I changed the API several times, breaking early clients. I’d use OpenAPI spec first and version the API from the start.“

“What was the hardest part?”

This is your chance to show problem-solving ability.

Good answer: Describe a specific technical challenge, how you approached it, and how you solved it.

“The hardest part was implementing distributed rate limiting. I needed rate limits that worked across multiple server instances. My first approach used Redis INCR with TTL, but it had a race condition — two requests could both read the counter below the limit and both increment, exceeding the limit.

I solved this by using Redis’s Lua scripting to make the check-and-increment atomic. The Lua script runs as a single atomic operation in Redis, so there’s no race condition. This was my first experience with Lua scripting in Redis, and I learned that atomic operations are critical for distributed coordination.“


“How does it scale?”

Show you understand scalability challenges.

“The current architecture scales horizontally for the read path — I can add more API server instances behind a load balancer, and they all read from the same Redis cache. The write path is more constrained because it goes through a single PostgreSQL instance.

To scale writes, I would:

  1. Use database sharding by URL hash — distribute URLs across multiple PostgreSQL instances
  2. Use a distributed ID generator (like Snowflake) instead of a database counter for short codes
  3. Add a write-ahead log for durability and async replication

The cache layer already handles most read traffic, so the database scaling is less urgent.“


“How do you handle failures?”

Show you’ve thought about reliability.

“I implemented several failure handling mechanisms:

  • Cache failure: If Redis is down, the application falls back to PostgreSQL directly. I added a circuit breaker to stop hammering Redis during outages.
  • Database failure: I set up PostgreSQL streaming replication with automatic failover using Patroni.
  • Application errors: All errors are logged with correlation IDs for debugging. I set up alerting on error rate spikes.
  • Rate limiting: I implemented rate limiting to prevent abuse, with proper 429 responses and Retry-After headers.“

The Trade-Off Discussion

Interviewers love to ask about trade-offs. Here’s a framework:

Trade-Off Matrix

For any decision, consider:

DimensionOption AOption B
Performance
Complexity
Cost
Maintainability
Time to implement
Scalability

Example: SQL vs. NoSQL

“For the URL shortener, I considered both PostgreSQL and DynamoDB:

PostgreSQL advantages: Strong consistency, familiar SQL queries, good for analytics, ACID transactions.

DynamoDB advantages: Automatic scaling, no operational overhead, millisecond latency at any scale.

I chose PostgreSQL because:

  1. I needed unique constraints on the original URL (easier in SQL)
  2. I wanted to run analytics queries (which URLs are most popular, geographic distribution)
  3. The scale I was targeting (10K req/s) was well within PostgreSQL’s capabilities
  4. I was more familiar with SQL, which meant faster development

At much higher scale (millions of URLs, 100K+ req/s), I’d reconsider DynamoDB because its auto-scaling would reduce operational burden.“


Presenting Unfinished Projects

It’s OK to discuss projects that aren’t complete. Frame it honestly:

“I built the core functionality — URL creation, redirect, and caching — but I didn’t finish the analytics dashboard. The project taught me the importance of scoping: I initially tried to build too many features and had to focus on the core use case first. The analytics is a natural next step, and I designed the schema to support it.”


Project Discussion Checklist

Before an interview, make sure you can answer these about each project on your resume:

  • Problem: Can you explain the problem in 30 seconds?
  • Architecture: Can you draw the architecture diagram from memory?
  • Decisions: Can you explain 3 key technical decisions and their trade-offs?
  • Challenges: Can you describe 2-3 specific technical challenges?
  • Alternatives: Can you explain what you considered but didn’t choose?
  • Scaling: Can you explain how it scales and where the bottlenecks are?
  • Failures: Can you explain how you handle failure scenarios?
  • Metrics: Do you have performance numbers (latency, throughput, scale)?
  • Learnings: Can you articulate what you learned?
  • Improvements: Can you explain what you’d do differently?

Anti-Patterns in Project Discussion

Anti-PatternWhy It’s BadBetter Approach
“I used React because it’s popular”Shows no critical thinking“I chose React because the app needed complex state management and the team was familiar with it”
“It was easy”Dismisses your own work“The core logic was straightforward, but making it production-ready required solving several interesting problems”
“I followed a tutorial”Shows you can follow instructions“I started with a tutorial to understand the basics, then extended it significantly by adding X, Y, Z”
“I don’t remember”Shows lack of engagement“Let me think about that… I believe the approach was X, but I’d need to check the code to be certain”
“It handles everything”Shows naivety about trade-offs“It handles the common case well, but I identified edge cases around X that would need additional work”
Listing technologies without contextShows breadth but no depthConnect each technology to a specific requirement or decision

Template: 2-Minute Project Pitch

Use this template to prepare a concise project explanation:

1. PROBLEM (15 sec):
   "I built [X] to solve [problem]. The key challenge was [challenge]."

2. ARCHITECTURE (30 sec):
   "The system has [components]. Data flows from [A] to [B] to [C].
   [Component] handles [responsibility]."

3. KEY DECISION (30 sec):
   "I chose [technology/approach] over [alternative] because [reason].
   The trade-off was [trade-off]."

4. CHALLENGE & SOLUTION (30 sec):
   "The hardest part was [challenge]. I solved it by [solution].
   This taught me [lesson]."

5. RESULT (15 sec):
   "The system handles [metric] with [performance].
   If I did it again, I'd [improvement]."

Example: Complete Project Discussion

Interviewer: “Tell me about a project you’re proud of.”

Candidate:

“I built a distributed rate limiter service that I deployed as an API gateway middleware for our team’s microservices.

The problem was that our services had no protection against traffic spikes or abusive clients. A single client could overwhelm a service by sending thousands of requests per second.

The architecture is straightforward: a Go service that sits between the client and the backend service. It checks each request against configured rate limits stored in Redis. I implemented three algorithms — token bucket, sliding window, and fixed window — because different endpoints have different traffic patterns. Token bucket works well for APIs with bursty traffic, while sliding window is better for strict per-minute limits.

The key decision was using Redis for distributed state rather than in-memory counters. The trade-off was added latency (about 1ms per request for the Redis call) versus the ability to share rate limit state across multiple gateway instances. For our scale, the 1ms overhead was acceptable.

The hardest challenge was making the rate limit check atomic in a distributed environment. My first implementation had a race condition where two concurrent requests could both pass the limit check. I solved this by using a Lua script in Redis that performs the read-check-increment as a single atomic operation.

The service handles about 10,000 requests per second with p99 latency under 3ms, including the Redis call. If I built it again, I’d add a local cache for rate limit counters to reduce Redis dependency — a small window of imprecision is acceptable for rate limiting, and it would make the service more resilient to Redis outages.“