Существует два фундаментальных подхода к проектированию систем на интервью: сверху вниз (top-down) и снизу вверх (bottom-up). Каждый имеет свои сильные стороны.
Top-Down (сверху вниз)
Начинаете с общей картины и постепенно детализируете.
Бизнес-требования
└─ Функциональные требования
└─ Высокоуровневая архитектура
└─ Компоненты
└─ Детали реализации
Когда использовать: для большинства System Design задач. Это наиболее естественный и понятный для интервьюера подход.
Bottom-Up (снизу вверх)
Начинаете с конкретных технических решений и собираете систему.
Структура данных
└─ API конкретного сервиса
└─ Взаимодействие сервисов
└─ Общая архитектура
└─ Соответствие требованиям
Когда использовать: когда у задачи есть очевидный ключевой компонент (например, алгоритм ранжирования для ленты новостей).
Как начинать: первые 5 минут
Первые 5 минут определяют качество всего интервью. Вот проверенный алгоритм.
Шаг 1: Переформулируйте задачу
<?php
declare(strict_types=1);
/**
* Interviewer says: "Design a URL shortener"
*
* Your restatement:
* "I will design a service that converts long URLs into short,
* unique identifiers, and redirects users from short URLs to originals.
* The system should handle high read traffic with low latency.
* Let me clarify the requirements."
*/
package shortener
// Interviewer says: "Design a URL shortener"
//
// Your restatement:
// "I will design a service that converts long URLs into short,
// unique identifiers, and redirects users from short URLs to originals.
// The system should handle high read traffic with low latency.
// Let me clarify the requirements."
namespace Shortener;
// Interviewer says: "Design a URL shortener"
//
// Your restatement:
// "I will design a service that converts long URLs into short,
// unique identifiers, and redirects users from short URLs to originals.
// The system should handle high read traffic with low latency.
// Let me clarify the requirements."
"""Interviewer says: "Design a URL shortener"
Your restatement:
"I will design a service that converts long URLs into short,
unique identifiers, and redirects users from short URLs to originals.
The system should handle high read traffic with low latency.
Let me clarify the requirements."
"""
### Шаг 2: Задайте уточняющие вопросы
Группируйте вопросы по категориям.
<?php
declare(strict_types=1);
/**
* Structured approach to clarifying questions
*/
final class RequirementQuestions
{
/**
* Category 1: Users and Scale
* - How many URLs shortened per day?
* - How many redirects per day?
* - What's the read/write ratio?
*/
public function getUserScaleQuestions(): array
{
return [
'How many new short URLs per day?' => '100M writes/day',
'How many redirects per day?' => '10B reads/day',
'Read/write ratio?' => '100:1 (read-heavy)',
'Geographic distribution?' => 'Global users',
];
}
/**
* Category 2: Functional Requirements
* - Custom aliases allowed?
* - Expiration policy?
* - Analytics needed?
*/
public function getFunctionalQuestions(): array
{
return [
'Custom aliases?' => 'Yes, optional',
'URL expiration?' => 'Default 5 years, configurable',
'Analytics (click count, geography)?' => 'Basic analytics',
'API or web interface or both?' => 'API primary',
];
}
/**
* Category 3: Non-Functional Requirements
* - Latency target?
* - Availability target?
* - Consistency requirements?
*/
public function getNonFunctionalQuestions(): array
{
return [
'Redirect latency target?' => '<100ms p99',
'Availability?' => '99.99%',
'URL uniqueness guarantee?' => 'Must be globally unique',
'Can short URL be reused after expiry?' => 'No, never reused',
];
}
}
package interview
// QA represents a clarifying question and its expected answer.
type QA struct {
Question string
Answer string
}
// UserScaleQuestions returns Category 1 questions about users and scale.
func UserScaleQuestions() []QA {
return []QA{
{"How many new short URLs per day?", "100M writes/day"},
{"How many redirects per day?", "10B reads/day"},
{"Read/write ratio?", "100:1 (read-heavy)"},
{"Geographic distribution?", "Global users"},
}
}
// FunctionalQuestions returns Category 2 questions about features.
func FunctionalQuestions() []QA {
return []QA{
{"Custom aliases?", "Yes, optional"},
{"URL expiration?", "Default 5 years, configurable"},
{"Analytics (click count, geography)?", "Basic analytics"},
{"API or web interface or both?", "API primary"},
}
}
// NonFunctionalQuestions returns Category 3 questions about SLAs.
func NonFunctionalQuestions() []QA {
return []QA{
{"Redirect latency target?", "<100ms p99"},
{"Availability?", "99.99%"},
{"URL uniqueness guarantee?", "Must be globally unique"},
{"Can short URL be reused after expiry?", "No, never reused"},
}
}
namespace Interview;
/// Structured approach to clarifying questions.
// A record gives value semantics for a question/answer pair.
public record QA(string Question, string Answer);
public static class RequirementQuestions
{
// Category 1: Users and Scale
public static IReadOnlyList<QA> UserScale() =>
[
new("How many new short URLs per day?", "100M writes/day"),
new("How many redirects per day?", "10B reads/day"),
new("Read/write ratio?", "100:1 (read-heavy)"),
new("Geographic distribution?", "Global users"),
];
// Category 2: Functional Requirements
public static IReadOnlyList<QA> Functional() =>
[
new("Custom aliases?", "Yes, optional"),
new("URL expiration?", "Default 5 years, configurable"),
new("Analytics (click count, geography)?", "Basic analytics"),
new("API or web interface or both?", "API primary"),
];
// Category 3: Non-Functional Requirements
public static IReadOnlyList<QA> NonFunctional() =>
[
new("Redirect latency target?", "<100ms p99"),
new("Availability?", "99.99%"),
new("URL uniqueness guarantee?", "Must be globally unique"),
new("Can short URL be reused after expiry?", "No, never reused"),
];
}
from dataclasses import dataclass
# Structured approach to clarifying questions.
@dataclass(frozen=True)
class QA:
"""A clarifying question and its expected answer."""
question: str
answer: str
# Category 1: Users and Scale
def user_scale_questions() -> list[QA]:
return [
QA("How many new short URLs per day?", "100M writes/day"),
QA("How many redirects per day?", "10B reads/day"),
QA("Read/write ratio?", "100:1 (read-heavy)"),
QA("Geographic distribution?", "Global users"),
]
# Category 2: Functional Requirements
def functional_questions() -> list[QA]:
return [
QA("Custom aliases?", "Yes, optional"),
QA("URL expiration?", "Default 5 years, configurable"),
QA("Analytics (click count, geography)?", "Basic analytics"),
QA("API or web interface or both?", "API primary"),
]
# Category 3: Non-Functional Requirements
def non_functional_questions() -> list[QA]:
return [
QA("Redirect latency target?", "<100ms p99"),
QA("Availability?", "99.99%"),
QA("URL uniqueness guarantee?", "Must be globally unique"),
QA("Can short URL be reused after expiry?", "No, never reused"),
]
### Шаг 3: Определите приоритеты
После получения ответов, явно расставьте приоритеты:
"Исходя из требований, основные приоритеты:
Низкая задержка редиректа (read path)
Высокая доступность
Глобальная уникальность URL
Аналитика -- второстепенна, может быть eventual consistent"
Искусство уточнения требований
Хорошие вопросы vs плохие
Плохой вопрос
Хороший вопрос
"Какую базу данных использовать?"
"Какой объем данных мы ожидаем?"
"Нужен ли Kubernetes?"
"Какая ожидаемая нагрузка?"
"Сколько серверов?"
"Какая допустимая задержка?"
"Нужен ли кэш?"
"Какое соотношение чтение/запись?"
Техника "сужения воронки"
<?php
declare(strict_types=1);
/**
* Funnel technique for requirement gathering
*
* Start broad, then narrow down
*/
// Level 1: Business context
// "What's the primary use case for this system?"
// Answer: "Short URLs for marketing campaigns and social media"
// Level 2: User behavior
// "Who creates URLs? End users or only our marketing team?"
// Answer: "Both, but 90% are from API integrations"
// Level 3: Scale
// "What's the expected daily volume?"
// Answer: "100M new URLs, 10B redirects per day"
// Level 4: Constraints
// "Any latency or availability requirements?"
// Answer: "Redirect must be <100ms, 99.99% uptime"
// Now you can make informed decisions:
final class DesignDecisions
{
/**
* Based on requirements:
* - 100:1 read/write ratio -> heavy caching strategy
* - <100ms redirect -> cache hot URLs in memory
* - 99.99% uptime -> multi-region deployment
* - 100M writes/day -> ~1200 writes/sec average
* - 10B reads/day -> ~115K reads/sec average
*/
public function summarize(): array
{
return [
'architecture' => 'Read-optimized with aggressive caching',
'storage' => 'Distributed KV store (DynamoDB/Cassandra)',
'caching' => 'Multi-tier: CDN + Redis + Application cache',
'id_generation' => 'Base62 encoding of distributed counter',
'deployment' => 'Multi-region active-active',
];
}
}
package interview
// Funnel technique for requirement gathering: start broad, then narrow down.
//
// Level 1: Business context
// "What's the primary use case for this system?"
// -> "Short URLs for marketing campaigns and social media"
//
// Level 2: User behavior
// "Who creates URLs? End users or only our marketing team?"
// -> "Both, but 90% are from API integrations"
//
// Level 3: Scale
// "What's the expected daily volume?"
// -> "100M new URLs, 10B redirects per day"
//
// Level 4: Constraints
// "Any latency or availability requirements?"
// -> "Redirect must be <100ms, 99.99% uptime"
// DesignDecisions summarizes architectural choices based on requirements.
// - 100:1 read/write ratio -> heavy caching strategy
// - <100ms redirect -> cache hot URLs in memory
// - 99.99% uptime -> multi-region deployment
// - 100M writes/day -> ~1200 writes/sec average
// - 10B reads/day -> ~115K reads/sec average
type DesignDecisions struct {
Architecture string // "Read-optimized with aggressive caching"
Storage string // "Distributed KV store (DynamoDB/Cassandra)"
Caching string // "Multi-tier: CDN + Redis + Application cache"
IDGeneration string // "Base62 encoding of distributed counter"
Deployment string // "Multi-region active-active"
}
namespace Interview;
// Funnel technique for requirement gathering: start broad, then narrow down.
//
// Level 1: Business context
// "What's the primary use case for this system?"
// -> "Short URLs for marketing campaigns and social media"
//
// Level 2: User behavior
// "Who creates URLs? End users or only our marketing team?"
// -> "Both, but 90% are from API integrations"
//
// Level 3: Scale
// "What's the expected daily volume?"
// -> "100M new URLs, 10B redirects per day"
//
// Level 4: Constraints
// "Any latency or availability requirements?"
// -> "Redirect must be <100ms, 99.99% uptime"
/// Summarizes architectural choices based on requirements.
// - 100:1 read/write ratio -> heavy caching strategy
// - <100ms redirect -> cache hot URLs in memory
// - 99.99% uptime -> multi-region deployment
// - 100M writes/day -> ~1200 writes/sec average
// - 10B reads/day -> ~115K reads/sec average
public record DesignDecisions(
string Architecture,
string Storage,
string Caching,
string IdGeneration,
string Deployment)
{
public static DesignDecisions Summarize() => new(
Architecture: "Read-optimized with aggressive caching",
Storage: "Distributed KV store (DynamoDB/Cassandra)",
Caching: "Multi-tier: CDN + Redis + Application cache",
IdGeneration: "Base62 encoding of distributed counter",
Deployment: "Multi-region active-active");
}
from dataclasses import dataclass
# Funnel technique for requirement gathering: start broad, then narrow down.
#
# Level 1: Business context
# "What's the primary use case for this system?"
# -> "Short URLs for marketing campaigns and social media"
#
# Level 2: User behavior
# "Who creates URLs? End users or only our marketing team?"
# -> "Both, but 90% are from API integrations"
#
# Level 3: Scale
# "What's the expected daily volume?"
# -> "100M new URLs, 10B redirects per day"
#
# Level 4: Constraints
# "Any latency or availability requirements?"
# -> "Redirect must be <100ms, 99.99% uptime"
@dataclass(frozen=True)
class DesignDecisions:
"""Architectural choices derived from the requirements.
- 100:1 read/write ratio -> heavy caching strategy
- <100ms redirect -> cache hot URLs in memory
- 99.99% uptime -> multi-region deployment
- 100M writes/day -> ~1200 writes/sec average
- 10B reads/day -> ~115K reads/sec average
"""
architecture: str
storage: str
caching: str
id_generation: str
deployment: str
def summarize() -> DesignDecisions:
return DesignDecisions(
architecture="Read-optimized with aggressive caching",
storage="Distributed KV store (DynamoDB/Cassandra)",
caching="Multi-tier: CDN + Redis + Application cache",
id_generation="Base62 encoding of distributed counter",
deployment="Multi-region active-active",
)
## Ведение диалога с интервьюером
Техника "Think Out Loud"
Проговаривайте свои мысли. Интервьюер не читает мысли.
<?php
declare(strict_types=1);
/**
* Example of thinking out loud during interview
*/
// "For ID generation, I see three options:
//
// Option 1: Auto-increment in database
// + Simple
// - Single point of failure
// - Predictable (security concern)
//
// Option 2: UUID
// + Distributed, no coordination
// - Too long for short URL (36 chars)
//
// Option 3: Base62 encoding of distributed counter
// + Short (7 chars for 3.5 trillion combinations)
// + Unpredictable with random offset
// - Needs counter service
//
// I'll go with Option 3 because we need short URLs
// and can tolerate the complexity of a counter service."
final class Base62Encoder
{
private const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
public function encode(int $number): string
{
if ($number === 0) {
return self::ALPHABET[0];
}
$result = '';
while ($number > 0) {
$result = self::ALPHABET[$number % 62] . $result;
$number = intdiv($number, 62);
}
return $result;
}
public function decode(string $code): int
{
$number = 0;
$length = strlen($code);
for ($i = 0; $i < $length; $i++) {
$number = $number * 62 + strpos(self::ALPHABET, $code[$i]);
}
return $number;
}
}
// 7 characters in base62 = 62^7 = 3,521,614,606,208 combinations
// At 100M/day, this lasts ~96 years
package shortener
import "strings"
// Think out loud during the interview:
//
// "For ID generation, I see three options:
//
// Option 1: Auto-increment in database
// + Simple
// - Single point of failure
// - Predictable (security concern)
//
// Option 2: UUID
// + Distributed, no coordination
// - Too long for short URL (36 chars)
//
// Option 3: Base62 encoding of distributed counter
// + Short (7 chars for 3.5 trillion combinations)
// + Unpredictable with random offset
// - Needs counter service
//
// I'll go with Option 3 because we need short URLs
// and can tolerate the complexity of a counter service."
const alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// Encode converts a number to a base62 string.
func Encode(number int64) string {
if number == 0 {
return string(alphabet[0])
}
var result []byte
for number > 0 {
result = append([]byte{alphabet[number%62]}, result...)
number /= 62
}
return string(result)
}
// Decode converts a base62 string back to a number.
func Decode(code string) int64 {
var number int64
for _, c := range code {
number = number*62 + int64(strings.IndexRune(alphabet, c))
}
return number
}
// 7 characters in base62 = 62^7 = 3,521,614,606,208 combinations
// At 100M/day, this lasts ~96 years
using System.Text;
namespace Shortener;
// Think out loud during the interview:
//
// "For ID generation, I see three options:
//
// Option 1: Auto-increment in database
// + Simple
// - Single point of failure
// - Predictable (security concern)
//
// Option 2: UUID
// + Distributed, no coordination
// - Too long for short URL (36 chars)
//
// Option 3: Base62 encoding of distributed counter
// + Short (7 chars for 3.5 trillion combinations)
// + Unpredictable with random offset
// - Needs counter service
//
// I'll go with Option 3 because we need short URLs
// and can tolerate the complexity of a counter service."
public static class Base62Encoder
{
private const string Alphabet =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Encode converts a number to a base62 string.
public static string Encode(long number)
{
if (number == 0)
{
return Alphabet[0].ToString();
}
var result = new StringBuilder();
while (number > 0)
{
result.Insert(0, Alphabet[(int)(number % 62)]);
number /= 62;
}
return result.ToString();
}
// Decode converts a base62 string back to a number.
public static long Decode(string code)
{
long number = 0;
foreach (var c in code)
{
number = number * 62 + Alphabet.IndexOf(c);
}
return number;
}
}
// 7 characters in base62 = 62^7 = 3,521,614,606,208 combinations
// At 100M/day, this lasts ~96 years
# Think out loud during the interview:
#
# "For ID generation, I see three options:
#
# Option 1: Auto-increment in database
# + Simple
# - Single point of failure
# - Predictable (security concern)
#
# Option 2: UUID
# + Distributed, no coordination
# - Too long for short URL (36 chars)
#
# Option 3: Base62 encoding of distributed counter
# + Short (7 chars for 3.5 trillion combinations)
# + Unpredictable with random offset
# - Needs counter service
#
# I'll go with Option 3 because we need short URLs
# and can tolerate the complexity of a counter service."
ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode(number: int) -> str:
"""Convert a number to a base62 string."""
if number == 0:
return ALPHABET[0]
chars: list[str] = []
while number > 0:
number, remainder = divmod(number, 62)
chars.append(ALPHABET[remainder])
return "".join(reversed(chars))
def decode(code: str) -> int:
"""Convert a base62 string back to a number."""
number = 0
for c in code:
number = number * 62 + ALPHABET.index(c)
return number
# 7 characters in base62 = 62^7 = 3,521,614,606,208 combinations
# At 100M/day, this lasts ~96 years
### Реагирование на подсказки
Интервьюер часто дает подсказки. Умение их распознавать -- важный навык.
Типы подсказок:
"А что будет, если..." -- вас подталкивают рассмотреть edge case
"Давайте предположим, что..." -- вас направляют к конкретному сценарию
"Есть ли альтернативы?" -- текущее решение не оптимально
"Как это будет работать при 10x нагрузке?" -- нужно обсудить масштабирование
Работа с "застреванием"
Если вы не знаете ответ:
Признайте: "Я не работал с этим напрямую, но вот мои рассуждения..."
Рассуждайте: примените общие принципы к конкретной проблеме
Спросите: "Можете дать подсказку в каком направлении думать?"
Практические шаблоны
Шаблон для начала любой задачи
<?php
declare(strict_types=1);
/**
* Template for starting any system design problem
*/
final class SystemDesignTemplate
{
public function start(string $problem): void
{
// 1. Restate the problem
$this->restate($problem);
// 2. Clarify requirements
$this->clarifyFunctional();
$this->clarifyNonFunctional();
$this->clarifyScale();
// 3. State assumptions
$this->stateAssumptions();
// 4. Identify core entities
$this->identifyEntities();
// 5. Draw high-level architecture
$this->drawArchitecture();
// 6. Deep dive into 2-3 components
$this->deepDive();
// 7. Discuss trade-offs
$this->discussTradeoffs();
}
/**
* Always state your assumptions explicitly.
* This shows maturity and prevents misunderstandings.
*/
private function stateAssumptions(): void
{
// "Let me state my assumptions:
// 1. We optimize for read performance over write
// 2. Eventual consistency is acceptable for analytics
// 3. We can use cloud infrastructure (AWS/GCP)
// 4. Budget is not the primary constraint
//
// Does this align with your expectations?"
}
}
package interview
// SystemDesignTemplate provides a structured approach to any problem.
//
// Steps:
// 1. Restate the problem
// 2. Clarify requirements (functional, non-functional, scale)
// 3. State assumptions
// 4. Identify core entities
// 5. Draw high-level architecture
// 6. Deep dive into 2-3 components
// 7. Discuss trade-offs
//
// Always state your assumptions explicitly:
// "Let me state my assumptions:
// 1. We optimize for read performance over write
// 2. Eventual consistency is acceptable for analytics
// 3. We can use cloud infrastructure (AWS/GCP)
// 4. Budget is not the primary constraint
//
// Does this align with your expectations?"
func Start(problem string) {
restate(problem)
clarifyFunctional()
clarifyNonFunctional()
clarifyScale()
stateAssumptions()
identifyEntities()
drawArchitecture()
deepDive()
discussTradeoffs()
}
namespace Interview;
/// Provides a structured approach to any problem.
///
/// Steps:
/// 1. Restate the problem
/// 2. Clarify requirements (functional, non-functional, scale)
/// 3. State assumptions
/// 4. Identify core entities
/// 5. Draw high-level architecture
/// 6. Deep dive into 2-3 components
/// 7. Discuss trade-offs
public sealed class SystemDesignTemplate
{
public void Start(string problem)
{
Restate(problem);
ClarifyFunctional();
ClarifyNonFunctional();
ClarifyScale();
StateAssumptions();
IdentifyEntities();
DrawArchitecture();
DeepDive();
DiscussTradeoffs();
}
// Always state your assumptions explicitly.
// This shows maturity and prevents misunderstandings.
private void StateAssumptions()
{
// "Let me state my assumptions:
// 1. We optimize for read performance over write
// 2. Eventual consistency is acceptable for analytics
// 3. We can use cloud infrastructure (AWS/GCP)
// 4. Budget is not the primary constraint
//
// Does this align with your expectations?"
}
}
"""Structured approach to any system design problem.
Steps:
1. Restate the problem
2. Clarify requirements (functional, non-functional, scale)
3. State assumptions
4. Identify core entities
5. Draw high-level architecture
6. Deep dive into 2-3 components
7. Discuss trade-offs
"""
def start(problem: str) -> None:
restate(problem)
clarify_functional()
clarify_non_functional()
clarify_scale()
state_assumptions()
identify_entities()
draw_architecture()
deep_dive()
discuss_tradeoffs()
def state_assumptions() -> None:
"""Always state your assumptions explicitly.
This shows maturity and prevents misunderstandings.
"Let me state my assumptions:
1. We optimize for read performance over write
2. Eventual consistency is acceptable for analytics
3. We can use cloud infrastructure (AWS/GCP)
4. Budget is not the primary constraint
Does this align with your expectations?"
"""
### Шаблон для обсуждения trade-offs
Каждое решение описывайте через плюсы, минусы и обоснование выбора.
<?php
declare(strict_types=1);
/**
* Trade-off analysis template
*/
final readonly class TradeoffAnalysis
{
/**
* @param array<string> $pros
* @param array<string> $cons
*/
public function __construct(
public string $option,
public array $pros,
public array $cons,
public string $verdict,
) {}
}
// Example usage during interview:
$options = [
new TradeoffAnalysis(
option: 'SQL Database (PostgreSQL)',
pros: [
'Strong consistency guarantees',
'ACID transactions',
'Rich query capabilities',
'Well-understood operational model',
],
cons: [
'Harder to scale horizontally',
'Schema changes can be expensive',
'Write throughput limited by single master',
],
verdict: 'Good for user profiles and metadata',
),
new TradeoffAnalysis(
option: 'NoSQL (Cassandra)',
pros: [
'Linear horizontal scaling',
'High write throughput',
'No single point of failure',
'Tunable consistency',
],
cons: [
'No joins or complex queries',
'Eventual consistency by default',
'Operational complexity',
],
verdict: 'Good for messages and time-series data',
),
];
package interview
// TradeoffAnalysis represents a structured comparison of options.
type TradeoffAnalysis struct {
Option string
Pros []string
Cons []string
Verdict string
}
// Example usage during interview:
var options = []TradeoffAnalysis{
{
Option: "SQL Database (PostgreSQL)",
Pros: []string{
"Strong consistency guarantees",
"ACID transactions",
"Rich query capabilities",
"Well-understood operational model",
},
Cons: []string{
"Harder to scale horizontally",
"Schema changes can be expensive",
"Write throughput limited by single master",
},
Verdict: "Good for user profiles and metadata",
},
{
Option: "NoSQL (Cassandra)",
Pros: []string{
"Linear horizontal scaling",
"High write throughput",
"No single point of failure",
"Tunable consistency",
},
Cons: []string{
"No joins or complex queries",
"Eventual consistency by default",
"Operational complexity",
},
Verdict: "Good for messages and time-series data",
},
}
namespace Interview;
/// Represents a structured comparison of options.
public record TradeoffAnalysis(
string Option,
IReadOnlyList<string> Pros,
IReadOnlyList<string> Cons,
string Verdict);
// Example usage during interview:
public static class Comparison
{
public static readonly List<TradeoffAnalysis> Options =
[
new(
Option: "SQL Database (PostgreSQL)",
Pros:
[
"Strong consistency guarantees",
"ACID transactions",
"Rich query capabilities",
"Well-understood operational model",
],
Cons:
[
"Harder to scale horizontally",
"Schema changes can be expensive",
"Write throughput limited by single master",
],
Verdict: "Good for user profiles and metadata"),
new(
Option: "NoSQL (Cassandra)",
Pros:
[
"Linear horizontal scaling",
"High write throughput",
"No single point of failure",
"Tunable consistency",
],
Cons:
[
"No joins or complex queries",
"Eventual consistency by default",
"Operational complexity",
],
Verdict: "Good for messages and time-series data"),
];
}
from dataclasses import dataclass
@dataclass(frozen=True)
class TradeoffAnalysis:
"""A structured comparison of options."""
option: str
pros: list[str]
cons: list[str]
verdict: str
# Example usage during interview:
options = [
TradeoffAnalysis(
option="SQL Database (PostgreSQL)",
pros=[
"Strong consistency guarantees",
"ACID transactions",
"Rich query capabilities",
"Well-understood operational model",
],
cons=[
"Harder to scale horizontally",
"Schema changes can be expensive",
"Write throughput limited by single master",
],
verdict="Good for user profiles and metadata",
),
TradeoffAnalysis(
option="NoSQL (Cassandra)",
pros=[
"Linear horizontal scaling",
"High write throughput",
"No single point of failure",
"Tunable consistency",
],
cons=[
"No joins or complex queries",
"Eventual consistency by default",
"Operational complexity",
],
verdict="Good for messages and time-series data",
),
]
Подготовьте 3-5 trade-off анализов для типичных решений
Выводы
Структурированный подход к решению System Design задач -- это навык, который тренируется. Начинайте с top-down подхода, всегда уточняйте требования и думайте вслух. Помните: интервьюер оценивает процесс вашего мышления, а не только финальный результат.