Google Site Reliability Engineer (Entry Level) - Complete Interview Preparation Guide
Google's Site Reliability Engineer interview process for entry-level candidates consists of a recruiter screening, two technical phone screens, and four on-site interview rounds. The process comprehensively evaluates your understanding of reliability engineering fundamentals, coding proficiency, system design basics, incident management, operational thinking, and cultural fit with Google. The entire process typically spans 6-8 weeks from initial contact to offer decision.
Interview Rounds
Recruiter Screening
What to Expect
Initial conversation with a Google recruiter to assess basic qualifications, discuss your background, and confirm mutual interest. This may be a single 30-minute conversation or two separate touchpoints (initial screen and follow-up). The recruiter will verify your background matches entry-level qualifications, discuss your motivation for the SRE role, explore your learning ability and foundational technical knowledge, and answer your questions about the position, team, and company culture. This is a screening stage designed to confirm you're a reasonable fit before investing time in technical interviews.
Tips & Advice
Be genuinely enthusiastic about the SRE role and Google. Prepare a concise 2-3 minute personal narrative covering your educational background, relevant technical experiences, and why you're interested in site reliability engineering specifically. Emphasize your strong fundamentals and eagerness to learn rather than claiming extensive experience (entry-level is expected). Ask specific, thoughtful questions about the team structure, the type of infrastructure they operate, what success looks like in the first 6 months, and onboarding process. Be upfront about your salary expectations if asked. Listen actively and show authentic curiosity about the role.
Focus Topics
Relevant Experience and Project Examples
Concrete examples from internships, projects, coursework, or personal work that demonstrate systems thinking, reliability considerations, automation, or DevOps exposure. This could include: projects involving infrastructure, monitoring, deployment pipelines, Linux systems, database design, or network concepts. Even non-SRE projects can demonstrate valuable thinking if framed appropriately.
Practice Interview
Study Questions
Genuine Interest in SRE and Google
Articulate why you're specifically interested in site reliability engineering—whether it's the combination of software engineering and systems work, the focus on automation and operational excellence, or the challenge of building reliable systems at scale. Show concrete knowledge of Google's scale, infrastructure challenges, and commitment to reliability. Avoid generic statements.
Practice Interview
Study Questions
Technical Foundation and Learning Ability
Demonstrate solid understanding of foundational computer science concepts: algorithms, data structures, Linux fundamentals, and basic networking. For entry-level, emphasize learning velocity, curiosity about systems, demonstrated growth in past projects, and willingness to rapidly skill up in unfamiliar areas. Discuss how you've learned new technologies or overcome knowledge gaps.
Practice Interview
Study Questions
Background and Career Narrative
Clear articulation of your educational background, relevant coursework, internships, projects, and career trajectory. Ability to explain how your experiences have prepared you for an SRE role. Demonstrated understanding of what SRE is and how your interests align with the role's responsibilities.
Practice Interview
Study Questions
Technical Phone Screen - Coding and Systems Fundamentals
What to Expect
First technical interview conducted via video call (typically 45-60 minutes) with a Google engineer. This round combines coding (approximately 20-30 minutes) and systems/infrastructure discussion (approximately 20-30 minutes). For the coding portion, you'll receive a medium-difficulty algorithm problem (approximately LeetCode Medium level) involving data structures and algorithmic thinking. For the systems portion, you'll answer questions about OS concepts, networking, databases, or foundational SRE concepts, with emphasis on your reasoning process. You'll use a shared Google Doc or online IDE. This round evaluates your ability to write clean, well-reasoned code, think systematically through problems, and communicate your thinking clearly.
Tips & Advice
For coding: Practice daily on LeetCode focusing on arrays, strings, linked lists, trees, graphs, and sorting problems at Medium difficulty. Aim for consistency over quantity. During the interview, verbally walk through your approach before coding. Test your solution with several examples including edge cases. For systems questions: Explain your reasoning clearly, ask for clarification when needed, and think out loud rather than staying silent. Discuss trade-offs in your answers. Write clean, readable code with proper naming. Manage time: aim to complete coding problem in 25-30 minutes leaving time for discussion. Be conversational and engage with the interviewer's hints.
Focus Topics
Operating Systems Fundamentals
Understanding of processes vs threads, context switching, memory management (stack vs heap), virtual memory, file systems basics, file descriptors, and system calls. Knowledge of basic Linux commands and system concepts. This is crucial foundational knowledge for SRE work.
Practice Interview
Study Questions
Code Quality, Communication, and Problem-Solving Process
Ability to write clean, readable code with meaningful variable names and appropriate comments. Strong communication throughout the interview: explaining your approach before coding, thinking out loud while solving, discussing complexity, and engaging with interviewer feedback. Demonstrated problem-solving methodology: understand requirements, design approach, implement step-by-step, test, and optimize.
Practice Interview
Study Questions
Networking Fundamentals
Understanding of TCP/IP protocol stack, DNS resolution, HTTP/HTTPS, sockets and socket programming basics, ports, and network communication. Familiarity with network troubleshooting tools and concepts like network latency, packet loss, and bandwidth.
Practice Interview
Study Questions
Data Structures and Their Applications
Deep understanding of arrays, linked lists, stacks, queues, hash tables, binary trees, binary search trees, graphs, and heaps. Know the strengths/weaknesses of each structure, typical operations and their complexity, and when to apply each. Ability to implement data structures from scratch. Understanding trade-offs between different structures for specific problems.
Practice Interview
Study Questions
String and Array Problem Solving
Practical ability to solve problems involving string manipulation, array operations, substring/subarray finding, two-pointer techniques, and sliding window approaches. Pattern recognition for common string and array problems.
Practice Interview
Study Questions
Sorting and Searching Algorithms
Solid understanding of common sorting algorithms (merge sort, quick sort, heap sort, insertion sort) with their time/space complexity and use cases. Knowledge of binary search and when to apply it. Ability to implement these algorithms cleanly and discuss trade-offs between approaches.
Practice Interview
Study Questions
Technical Phone Screen - SRE Fundamentals and Systems Thinking
What to Expect
Second technical phone interview (approximately 45-60 minutes) with a Google SRE engineer focusing on SRE-specific concepts, infrastructure thinking, and practical problem-solving. You'll receive scenario-based questions that assess SRE fundamentals and approach to systematic problem-solving. Examples might include: 'How would you troubleshoot a service that's experiencing high latency?' 'What metrics would you monitor for a web service and why?' 'Walk me through your approach to incident response' 'How would you identify whether a system needs to scale?' This round focuses on your SRE thinking, understanding of reliability engineering principles, and practical approach to operations problems. The interviewer is assessing whether you think like an SRE and can apply SRE principles to real scenarios.
Tips & Advice
Thoroughly study SRE fundamentals before this interview. For each scenario, ask clarifying questions before diving into answers—understand the context and constraints first. Think systematically through problems: gather information, form hypotheses, test, and iterate. Discuss what you'd monitor, measure, and how you'd know if your solution worked. Be honest about entry-level gaps in specific tools or domain knowledge, but show how you'd approach learning those areas. Share any relevant project work, coursework, or personal experience. Emphasize your systems thinking and curiosity about 'how things work.' Discuss trade-offs explicitly. Show blameless incident thinking and focus on continuous improvement.
Focus Topics
Automation and Infrastructure-as-Code Principles
Understanding why automation matters for reliability and scalability—reducing manual work reduces errors and enables consistent, repeatable processes. Knowledge of scripting basics (Bash, Python, Go), infrastructure-as-code concepts, configuration management principles, and CI/CD pipeline fundamentals. Understanding the value of automation in incident response, deployments, and operational tasks.
Practice Interview
Study Questions
Linux Systems Administration and Troubleshooting
Practical Linux knowledge: file systems, processes and process management, resource management (CPU, memory, I/O), permissions and security, networking tools (ifconfig, netstat, ss, tcpdump), system logs and log analysis, package management, and basic performance analysis. Ability to diagnose common system issues and understand system behavior through commands and tools.
Practice Interview
Study Questions
Systems Thinking and Systematic Problem-Solving
Ability to approach problems systematically: gathering information before concluding, identifying failure modes, assessing impact, determining root causes vs symptoms, and implementing targeted solutions. Understanding systems thinking: how components interact, where failure points exist, and how changes propagate. Comfort with uncertainty and willingness to investigate carefully rather than guessing.
Practice Interview
Study Questions
Monitoring, Observability, and Effective Alerting
Understanding comprehensive monitoring strategies and metric selection (RED method: Rate of requests, Errors, Duration; or USE method: Utilization, Saturation, Errors). Knowledge of logs, metrics, traces, and structured logging practices. Understanding the difference between monitoring (collecting data to assess health) and observability (ability to understand system state from external observations). Alerting best practices: meaningful thresholds, avoiding alert fatigue, actionable alerts. Awareness of tools like Prometheus, Grafana, ELK stack, Datadog, and Google Cloud Monitoring.
Practice Interview
Study Questions
Service Level Objectives (SLOs), Indicators (SLIs), and Error Budgets
Understanding the distinction and relationship between SLOs (target reliability goals set by SRE/product), SLIs (measured signals indicating service health like latency, error rate, availability), and SLAs (contractual commitments). Knowledge of how to identify meaningful SLIs, set realistic and ambitious SLOs, and use error budgets to make release decisions. Understanding how error budgets balance feature velocity with reliability—when error budget is plentiful, teams can innovate freely; when exhausted, focus shifts to reliability.
Practice Interview
Study Questions
Incident Response, Troubleshooting, and Learning from Failures
Understanding incident response processes: severity levels, escalation procedures, communication during incidents, and the role of various team members. Knowledge of Mean Time to Detection (MTTD), Mean Time to Recovery (MTTR), and their importance. Understanding blameless postmortem culture: how to analyze failures to learn and improve without assigning blame. Systematic troubleshooting approach: gathering information, forming hypotheses, testing, determining root causes.
Practice Interview
Study Questions
On-site Round 1: Technical Coding Interview
What to Expect
First on-site interview (approximately 50-60 minutes) with a Google engineer focused on coding, algorithms, and problem-solving. You'll receive one or two coding problems at approximately LeetCode Medium difficulty. This round closely mirrors the phone screen coding component but with more intensive focus and potentially slightly higher difficulty. You may code on a laptop, shared document, or whiteboard depending on circumstances. The interviewer will assess your algorithm design skills, code quality, ability to optimize, edge case handling, and communication throughout the process. You'll be evaluated on correctness, code clarity, efficiency, and your problem-solving approach.
Tips & Advice
Practice whiteboard coding even if you'll use a laptop on-site. For each problem: (1) Understand requirements thoroughly—ask clarifying questions. (2) Think out loud about your approach before coding—get interviewer feedback early. (3) Start with a correct brute-force solution rather than attempting optimization prematurely. (4) Test your solution with multiple examples including edge cases. (5) Optimize if time permits, but ensure correctness first. (6) Discuss time and space complexity clearly. Handle edge cases proactively—don't wait for the interviewer to point them out. Write clean code with meaningful variable names. If stuck, explain your thinking and ask for hints rather than sitting silent. Be prepared to modify your approach based on interviewer feedback.
Focus Topics
Code Quality, Readability, and Maintainability
Writing clean, readable code with meaningful variable names, appropriate comments where helpful, proper indentation and formatting, and logical structure. Producing code that's easy to understand and modify. Avoiding unnecessarily clever solutions in favor of clarity.
Practice Interview
Study Questions
Edge Cases, Boundary Conditions, and Error Handling
Proactively identifying edge cases in problems (empty inputs, single elements, boundary values, negative numbers, duplicates, very large inputs, etc.) and handling them correctly. Anticipating common pitfalls and avoiding them. Demonstrating mature thinking about robustness.
Practice Interview
Study Questions
Time and Space Complexity Analysis and Optimization
Ability to analyze algorithm complexity in Big O notation, articulate time and space requirements clearly, understand implications for different input sizes, and optimize solutions when appropriate. Knowledge of when to prioritize time vs space, and trade-offs between approaches.
Practice Interview
Study Questions
Algorithm Design and Implementation Proficiency
Ability to design algorithms from first principles, identify which data structures and patterns apply to different problem types, implement solutions cleanly, and discuss trade-offs between different approaches. Recognizing common algorithm patterns and knowing when to apply them.
Practice Interview
Study Questions
Systematic Problem-Solving Methodology
Structured approach to tackling coding problems: (1) understand the problem completely, identify constraints and examples; (2) identify the problem pattern or type; (3) design algorithm from first principles; (4) implement step-by-step; (5) test with examples and edge cases; (6) optimize if appropriate. Ability to communicate your thinking verbally throughout and adjust based on feedback.
Practice Interview
Study Questions
On-site Round 2: System Design - Foundational Concepts
What to Expect
On-site interview (approximately 50-60 minutes) with a Google engineer focusing on basic system design and architecture thinking. You'll receive a simplified system design problem such as 'Design a URL shortener,' 'Design a simple monitoring system,' 'Design a basic caching layer,' or similar. For entry-level, the focus is on your ability to think through systems systematically, make reasonable architectural decisions, discuss trade-offs explicitly, communicate your approach clearly, and show awareness of reliability concerns. You're not expected to handle extreme scale or have perfect solutions, but to demonstrate solid foundational thinking. You should be able to draw architecture diagrams, explain component interactions, and discuss your design rationale. Engagement with interviewer feedback matters—show how you'd adjust your design based on constraints.
Tips & Advice
Start by asking clarifying questions about requirements and constraints before diving into design. Don't assume extreme scale—understand requirements first. Draw diagrams to visualize your architecture. Start with a simple, correct design before optimizing. Discuss components explicitly: how they interact, what data they store, how they handle failures. Explicitly discuss trade-offs (consistency vs availability, latency vs throughput, cost vs reliability, simplicity vs sophistication). For entry-level, show your thinking rather than attempting perfection. Identify potential bottlenecks and discuss approaches to handle them. Be honest about uncertainties. Engage genuinely with interviewer questions and be willing to revise your design. Ask follow-up questions to clarify interviewer's perspective.
Focus Topics
Trade-offs in System Design and Decision-Making
Ability to discuss fundamental trade-offs in system design: consistency vs availability (CAP theorem at basic level), latency vs throughput, cost vs reliability, simplicity vs sophistication, centralized vs distributed. Making reasoned decisions about trade-offs based on requirements and constraints.
Practice Interview
Study Questions
Data Management and Database Selection
Basic understanding of SQL vs NoSQL databases, when to use each, replication and consistency models, partitioning/sharding concepts, indexing basics, and query optimization at high level. Understanding data durability and backup strategies.
Practice Interview
Study Questions
Clear Communication and Architecture Documentation
Ability to clearly communicate your design ideas through diagrams (boxes and arrows showing components), explaining component roles and interactions, articulating your reasoning for design choices, and discussing your design's strengths and limitations. Using vocabulary appropriately and explaining concepts clearly.
Practice Interview
Study Questions
Reliability, Fault Tolerance, and Failure Handling
Understanding reliability principles: redundancy, failover mechanisms, graceful degradation, and how systems remain operational despite failures. Concepts like replication for fault tolerance, backup systems, health checks, circuit breakers. Understanding failure modes and designing systems resilient to failures.
Practice Interview
Study Questions
System Architecture Fundamentals and Design Patterns
Understanding fundamental architectural patterns: monolithic vs microservices trade-offs, layered architecture (presentation, business logic, data layers), client-server architecture, and distributed system patterns. Knowing when each pattern is appropriate and implications of each choice.
Practice Interview
Study Questions
Scalability, Load Balancing, and Horizontal vs Vertical Scaling
Understanding scalability strategies: vertical scaling (bigger servers, more resources) vs horizontal scaling (more servers). Load balancing concepts for distributing traffic. Database scaling approaches: replication, sharding, read replicas. Trade-offs between approaches: horizontal scaling complexity vs simplicity, replication consistency considerations, etc.
Practice Interview
Study Questions
On-site Round 3: SRE Technical Deep Dive - Operations and Reliability Engineering
What to Expect
On-site interview (approximately 50-60 minutes) with a Google SRE engineer diving deeper into SRE-specific technical challenges, incident management, and operational thinking. This round goes beyond fundamentals to assess your practical SRE approach and systems understanding. You may receive scenario-based questions like: 'How would you systematically debug a memory leak in production?' 'What would you monitor for a distributed web service and why?' 'Walk me through your complete incident response process,' 'How would you approach performance optimization for a slow system?' or 'Design monitoring for a critical service.' This round assesses your SRE fundamentals, systematic thinking approach, practical problem-solving skills, and whether you understand real-world operational challenges.
Tips & Advice
Deeply understand SRE concepts before the interview—this is non-negotiable for this round. When given scenarios, ask clarifying questions and think systematically through the problem, explaining your reasoning step-by-step. Discuss your approach: what you'd check, what tools you'd use, what you'd measure, and how you'd know when you've solved the problem. Show comfort with uncertainty—acknowledge what you don't know but explain how you'd approach learning. Share relevant personal projects, coursework, or internship experiences that demonstrate SRE thinking. Discuss post-incident learning, continuous improvement, and blameless postmortem culture. Connect your technical thinking to reliability outcomes. For entry-level, show fundamental understanding and thoughtful approach rather than claiming deep expertise.
Focus Topics
Distributed Systems Challenges and Reliability Patterns
Understanding challenges specific to distributed systems: partial failures, network partitions, eventual consistency, consensus, distributed tracing. Failure modes in distributed systems and patterns for handling them: retries with backoff, circuit breakers, bulkheads, graceful degradation. Distributed tracing for understanding system behavior across services.
Practice Interview
Study Questions
Error Budgets and Release Management
Understanding error budgets: how they're calculated from SLOs, tracked over time periods, and consumed by incidents or risky changes. How error budgets inform release decisions: plentiful budget enables feature releases, exhausted budget prioritizes reliability work. Communication between SRE and development teams about error budget status and implications.
Practice Interview
Study Questions
Automation, Infrastructure-as-Code, and Operational Excellence
Automation of routine tasks reduces toil and errors. Infrastructure-as-code principles for reproducible, testable infrastructure changes. Configuration management tools and practices. Continuous integration/deployment pipelines. Runbooks and operational procedures. Reducing manual work enables SRE focus on reliability improvement.
Practice Interview
Study Questions
Systematic Incident Response and Troubleshooting Methodology
Structured approach to debugging and troubleshooting: (1) gather information about the incident, (2) understand symptoms vs root cause, (3) form hypotheses, (4) test hypotheses systematically, (5) determine root cause, (6) implement fix, (7) verify resolution. Understanding incident severity levels, escalation procedures, communication protocols during incidents. Knowledge of Mean Time to Detection (MTTD), Mean Time to Recovery (MTTR), and their importance to user experience. Understanding blameless postmortem culture and learning from incidents to prevent recurrence.
Practice Interview
Study Questions
Comprehensive Monitoring, Observability, and Alerting Strategy
Designing comprehensive monitoring for systems: selecting meaningful metrics, setting appropriate thresholds, creating effective dashboards, implementing alerting without alert fatigue. Understanding observability (ability to understand system state from external observations) vs monitoring. Using logs, metrics, traces for system understanding. Implementing structured logging. Understanding when to alert vs when to track metrics. Choosing appropriate tools based on system needs.
Practice Interview
Study Questions
Performance Analysis, Optimization, and Capacity Planning
Identifying performance bottlenecks through analysis and profiling. Understanding performance metrics and tools. Optimization strategies at different levels (code, configuration, architecture). Capacity planning: forecasting growth, planning ahead, handling peak loads. Horizontal vs vertical scaling decisions. Performance optimization mentioned explicitly in the job description.
Practice Interview
Study Questions
On-site Round 4: Behavioral and Cultural Fit
What to Expect
Final on-site interview (approximately 45-60 minutes) assessing behavioral fit and cultural alignment. You'll be asked about your past experiences, how you handle challenges, work in teams, learn from failures, and whether you align with Google's values. This round uses behavioral questions with the STAR method (Situation, Task, Action, Result) to assess soft skills, teamwork, communication, problem-solving approach, and cultural fit. Typical questions cover: times you've failed and what you learned, worked effectively in teams, handled conflict or disagreement, showed initiative, dealt with ambiguous situations, pursued excellence, learned something new, or overcame challenges. This round evaluates your growth mindset, learning agility, collaboration skills, and alignment with Google's culture and values.
Tips & Advice
Prepare 5-6 strong STAR method stories from your experience (projects, internships, coursework, personal projects). Frame stories appropriately for entry-level—emphasize learning, collaboration, problem-solving, and growth mindset rather than leadership. For each story: clearly set the Situation and Task, explain specific Actions you took, and articulate Results achieved and what you learned. Prepare for entry-level appropriately: focus on foundational skills like learning ability, teamwork, initiative, receiving feedback, and growth. Be authentic rather than trying to project an idealized profile. Discuss how you've learned from failures with genuine reflection. Research Google's culture, values, and products. Ask thoughtful questions about team culture, how they operate, onboarding process, and opportunities for growth. Listen actively to interviewer and engage genuinely with their experiences.
Focus Topics
User Focus and Impact Orientation
Understanding how your work affects users and systems, thinking about user impact in your decisions, and prioritizing work based on what provides most value. Demonstrated awareness of business context and user needs in your work.
Practice Interview
Study Questions
Clear Communication and Adaptability
Ability to communicate ideas clearly to different audiences, explain technical concepts simply, listen actively, ask clarifying questions, and adjust communication style to audience. Demonstrated ability to avoid jargon and ensure understanding. Flexibility in adjusting approach based on feedback.
Practice Interview
Study Questions
Taking Initiative and Ownership
Examples of identifying problems proactively rather than waiting to be told, taking ownership of solutions, following through on commitments, and driving improvements. Stories showing initiative without being asked, responsibility even when tasks weren't formally assigned.
Practice Interview
Study Questions
Handling Failure, Resilience, and Learning from Setbacks
Mature handling of setbacks and failures, extracting genuine learning, maintaining resilience under pressure, and recovering from mistakes constructively. Stories where things didn't go as planned but you responded well, learned important lessons, and improved as a result. Avoiding defensive responses to failure and demonstrating genuine reflection.
Practice Interview
Study Questions
Learning Agility, Growth Mindset, and Continuous Development
Demonstrated ability and genuine desire to learn new concepts, adapt to new environments, and grow as an engineer. For entry-level, this is critical. Examples of tackling unfamiliar problems, learning new technologies independently, taking on stretch assignments, and continuous growth. Stories showing your learning approach and how you've built skills.
Practice Interview
Study Questions
Teamwork, Collaboration, and Communication
Ability to work effectively with others, communicate clearly and proactively, respect different perspectives, listen actively, and contribute to team success. Stories demonstrating how you've collaborated with teammates on projects, resolved conflicts constructively, helped teammates succeed, or worked across different teams. Showing appreciation for diverse viewpoints and ability to influence through collaboration rather than authority.
Practice Interview
Study Questions
Frequently Asked Site Reliability Engineer (SRE) Interview Questions
Write a function to reverse a string. The function should accept either a mutable character array (e.g., char[] in C++/Java) and reverse it in-place, or when using a language with immutable strings (e.g., Python), return a new reversed string. Include handling for empty input and single-character strings. Example: input = "hello", output = "olleh". Discuss time and space complexity and briefly mention how you'd handle Unicode surrogate pairs or combining marks.
Sample Answer
Direct answer
In a language with a mutable character buffer (char[] in C++ or Java), reverse in place with the same two-pointer swap used for arrays. In a language with immutable strings, like Python's str, in-place mutation is not possible at all, so the function has to build and return a brand-new string, most simply by slicing (s[::-1]). Handling non-ASCII text correctly is a separate concern from which of these two shapes you write, and it has two distinct failure modes worth naming explicitly.
Structured elaboration
Mutable buffer vs immutable string. The char[] version is identical in structure to reversing an array: two pointers, swap, converge. The Python version cannot mutate the input at all, s[::-1] (or building a list, swapping, and joining) allocates a new string object, so the complexity is still O(n) time but now genuinely O(n) space for the output, not O(1), since a mutable in-place buffer is not available.
Surrogate pairs. A surrogate pair is how UTF-16 represents a character outside the Basic Multilingual Plane (for example, most emoji): as a HIGH surrogate code unit immediately followed by a LOW surrogate code unit, two 16-bit units standing for one character. Python's str is a sequence of Unicode code points, not UTF-16 code units, so a code-point-wise reversal in Python does not split such a character; this specific failure mode is really a concern for UTF-16-based languages (Java char[], JavaScript, C# char[]), where an index-based swap over raw code units can split a surrogate pair in two and corrupt the character unless the swap logic explicitly detects and moves whole surrogate pairs together.
Combining marks. This one does apply inside Python. A combining mark is a Unicode character, most often an accent, that attaches to the base character immediately before it rather than standing on its own (unicodedata.combining(ch) != 0 identifies one). Reversing code point by code point moves a combining mark in front of the wrong letter: given "e" followed by a combining acute accent (rendering as "é"), followed by "f" (the whole thing rendering as "éf"), a naive reversal produces the code points in the order f, accent, e, which renders as an accent floating over "f" followed by a plain "e", not "fé". The fix is to group each base character with any combining marks that trail it into one cluster before reversing, and reverse the order of clusters rather than the order of raw code points.
Worked example
import unicodedata
def reverse_char_array_in_place(chars):
left, right = 0, len(chars) - 1
while left < right:
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1
def reverse_string(s: str) -> str:
return s[::-1]
def _grapheme_clusters(s: str):
clusters, current = [], []
for ch in s:
if unicodedata.combining(ch) and current:
current.append(ch)
else:
if current:
clusters.append(''.join(current))
current = [ch]
if current:
clusters.append(''.join(current))
return clusters
def reverse_grapheme_aware(s: str) -> str:
return ''.join(reversed(_grapheme_clusters(s)))
chars = list("hello")
reverse_char_array_in_place(chars)
print("char[] reverse: 'hello' ->", "".join(chars))
print("reverse_string('hello') ->", reverse_string("hello"))
print()
for edge in ["", "a"]:
edge_chars = list(edge)
reverse_char_array_in_place(edge_chars)
joined = ''.join(edge_chars)
print(f"{edge!r} -> {joined!r} | {reverse_string(edge)!r}")
print()
astral = "a" + chr(0x1F600) + "b"
print("astral reverse:", repr(astral), "->", repr(reverse_string(astral)))
print()
e_acute_decomposed = "e" + chr(0x0301) + "f"
print("input:", repr(e_acute_decomposed))
naive = reverse_string(e_acute_decomposed)
ga = reverse_grapheme_aware(e_acute_decomposed)
print("naive code-point reverse: ", repr(naive))
print("grapheme-aware reverse: ", repr(ga))
precomposed_fe = "f" + chr(0x00E9)
nfc_match = unicodedata.normalize("NFC", ga) == unicodedata.normalize("NFC", precomposed_fe)
print("NFC(grapheme-aware reversed) == NFC(" + repr(precomposed_fe) + "):", nfc_match)
Output:
char[] reverse: 'hello' -> olleh
reverse_string('hello') -> olleh
'' -> '' | ''
'a' -> 'a' | 'a'
astral reverse: 'a😀b' -> 'b😀a' # surrogate-pair-safe: the emoji stays one intact character
input (renders as 'ef with acute on e', i.e. 'ef'-with-accent): 'éf'
naive code-point reverse: 'f́e' (renders as accent floating on 'f', WRONG)
grapheme-aware reverse: 'fé' (renders as the correct 'fe'-with-accent-on-e)
NFC(grapheme-aware reversed) == NFC('fé'): True
Empty input and a single character are demonstrated directly above (both round-trip unchanged in both the mutable and immutable versions), since the swap loop never executes when left < right is false immediately. The astral-character case confirms Python's code-point-based reversal never splits an emoji-style character. The combining-mark case is the one that actually breaks under naive reversal: the accent ends up on the wrong letter under s[::-1], and the grapheme-aware version fixes it, confirmed by normalizing both the grapheme-aware result and the independently-written precomposed form ("f" + precomposed é) to the same NFC (Normalization Form Canonical Composition, the Unicode-standard form that prefers a single precomposed character over a base-plus-accent pair) representation and finding them equal.
Trade-offs and pitfalls
The grapheme-cluster heuristic shown here (base character plus any immediately trailing combining marks) handles the common accented-letter case, but it is not a complete implementation of Unicode's grapheme-cluster segmentation rules: genuinely full correctness, including cases like emoji joined with zero-width joiners or scripts that stack multiple combining marks, needs a real text-segmentation library, not this simplified heuristic.
The core mistake to avoid stating out loud in an interview is conflating "my language's string type is safe from splitting a multi-code-unit character" with "my output is visually correct." Python's code-point safety on surrogate pairs is real, but it does not extend to combining marks, those are a separate and independent failure mode.
Name explicitly which layer you mean by "reverse this string": raw bytes, UTF-16 code units, Unicode code points, or grapheme clusters. "Reverse this string" is genuinely ambiguous exactly at this boundary the moment the input can contain non-ASCII text, and naming the layer up front is what separates a careful answer from a lucky one.
A stakeholder sends you a short, vague request (for example, 'make this better' or 'we need improved reporting'). List the clarifying questions you would ask to turn it into something specific and actionable before you commit to any plan.
Sample Answer
Direct answer
Turn a vague request into something actionable by asking about the underlying problem, the scope, the success criteria, the constraints, and the audience, before committing to any plan.
Structured elaboration
- Underlying problem: "What made you raise this now? What's the actual pain point behind 'make this better'?" A vague request almost always has a specific trigger; find it.
- Scope: "Better for whom, and for which part of the system or process? Everyone, or a specific segment?"
- Success criteria: "How will we know this worked? Is there a metric, or is it a qualitative judgment?"
- Constraints: "What's the timeline, budget, or team capacity for this? Is there a deadline driving the request?"
- Priority relative to other work: "Where does this rank against what's already committed?"
- Audience/stakeholders: "Who else cares about the outcome, and does anyone need to sign off?"
Ask these as a short, prioritized set, not all twelve possible questions at once; pick the three or four that would most change your plan if the answer were different.
Worked example
Request: "We need improved reporting."
Clarifying questions actually asked, in order: "What decision is the current reporting failing to support, what's an example of a time it fell short?" "Is this for internal use, or does it go to customers or leadership?" "Is there a specific number or turnaround time we're trying to hit, or is this more about it being easier to use?" "Is there a deadline tied to this, like a board meeting or a renewal?"
The answers turn "improved reporting" into something like "leadership wants same-day revenue numbers instead of the current 3-day lag, ahead of next month's board meeting," which is now a scoped, testable requirement instead of a vague preference.
Trade-offs and pitfalls
- Asking too many questions at once, or overly generic ones ("can you tell me more?"), reads as not having engaged with the request; targeted questions that show you've already thought about what would change your plan land better.
- Sometimes the requester genuinely doesn't know the answer either (they're relaying a vague ask from someone else); in that case, propose a specific, falsifiable interpretation ("I'll assume you mean X unless you tell me otherwise") rather than blocking on an answer that may never come.
- There's a real cost to over-clarifying trivial requests; match the depth of questioning to the size of the commitment you're about to make.
Share an example of letting a customer down or falling short of what you had promised them. How did you recover, and what did you change afterward?
Sample Answer
Direct answer
A strong answer owns the miss without excuses, shows what I did fast to limit harm (tell the customer early, give an interim fix, coordinate engineering with one owner), what I changed so it would not recur, and how the relationship ended up. The example below is an illustrative skeleton; use your own facts.
Situation
I had told a customer an integration would be ready by their launch date. Two weeks before, it became clear the date was not safe, because engineering found an unexpected limit in a third-party service.
Recovery
- Tell them early, directly. I called the customer's lead as soon as I knew, rather than waiting for the date to pass. I said what I had promised, what had changed, and that the fault was in how I committed.
- Offer an interim path. A manual workaround covered their launch while the integration was finished.
- Coordinate engineering. I became the single point of contact (one person the customer can always reach): one shared channel, a named engineering owner, and a status update to the customer every few days even when nothing had changed.
- Give a new date with an honest range. I committed only after engineering confirmed it, stating a range and what could move it.
The same story from a reliability seat
If your seat is site reliability engineering, the promise is usually a service level agreement (SLA, the uptime or recovery commitment written into the contract) rather than a launch date, but the recovery has the same shape. Say the service fell below the promised uptime. You post a status update as soon as you know, not after the customer notices. You put in a mitigation (a temporary fix such as failing over to a standby system) while the real fix is built, and you run one incident owner so the customer talks to one person. You then give an honest estimate of the fix, and afterwards run a blameless post-incident review (a written account of what happened and why, aimed at fixing the process, not blaming a person) with named action items such as better alerting or a capacity limit check.
Turning the customer around
The customer was frustrated at first, but the early warning, the workaround and the regular updates gave them what they needed to plan. They later agreed to act as a reference (a customer who vouches for you to prospective customers) for the integration, which is the outcome I aim for: a detractor (an unhappy customer; in the Net Promoter Score survey, someone answering 6 or lower on the 0 to 10 "would you recommend us" question) becoming an advocate because of how a problem was handled.
What I changed afterwards
- I stopped promising dates before engineering confirmed an estimate, and began giving ranges.
- I added a checkpoint at the midpoint of any customer commitment to check confidence.
- I wrote a short review of what went wrong and shared it with the team.
Pitfalls
Blaming engineering or the customer, minimising the miss, claiming a recovery you cannot show, or describing only the apology. The strongest answers show the specific process change. A strong story also shows the specific process change, since an apology alone does not stop a repeat.
You're designing a user-profile service requiring flexible attributes, low-latency reads for 100M users, and occasional complex analytics queries. Compare using a relational DB versus a document NoSQL store for serving the profile API. Discuss data modeling, consistency, read and write patterns, caching needs, operational cost, and a migration path that supports both fast reads and analytics workloads.
Sample Answer
Direct answer
Serve the profile API from a document-oriented NoSQL store (DynamoDB or MongoDB, either fits this shape) rather than a relational database, because flexible per-user attributes and low-latency reads at 100 million users are exactly what a document store's native partitioning and schemaless record shape were built for. Never make that same store answer the "occasional complex analytics" requirement directly: feed a change-data-capture (CDC, a stream of every insert/update/delete as it happens) pipeline into a columnar analytical warehouse instead, so analytical scans never compete with the serving path for resources.
Structured elaboration
Why document over relational for the serving path. A relational schema for "flexible attributes" forces a choice between a wide, mostly-empty table (most columns null for most users, wasting storage and confusing every query planner statistic) or a key-value attribute table (user_id, attribute_name, attribute_value), which turns every profile read into a self-join and reintroduces exactly the problem a document store solves natively. A document model stores each user's profile, whatever shape it happens to have, as one self-describing record. Reading one profile is one partition read, not a join.
Data modeling. One document per user, keyed by user_id. Embed attributes that are almost always read together with the base profile (display name, avatar, preference flags); keep unbounded, ever-growing collections (activity history, full audit log) out of the profile document entirely, in a separate store keyed by user_id plus a time or sequence component, so the profile document itself stays small and cheap to read.
Consistency. The UX moment that matters is "I just edited my profile, does the app show my edit." That needs read-your-writes on the write path: use a strongly consistent read (DynamoDB's ConsistentRead=true, or MongoDB's read preference set to the primary) immediately after a write, and fall back to eventually consistent reads everywhere else, including any read replicas, since a moment of staleness on someone else's profile view is not user-visible or harmful.
Read and write pattern. The stated shape is read-heavy: 100 million users implies far more profile views than profile edits. That is the textbook case for a cache-aside layer.
Caching. Put Redis in front as a cache-aside layer: a read checks Redis first, falls through to the document store on a miss and populates the cache, and a write invalidates (or updates) the cached copy. A time-to-live (TTL, an automatic expiration the cache enforces) bounds how stale an uninvalidated entry can get as a safety net.
Operational cost. A managed document store's cost scales with actual request volume and storage, which is usually cheaper at this DAU-driven traffic shape than provisioning a relational read-replica fleet sized for the same peak, because the document store's partitioning is native and automatic rather than a fleet you provision and rebalance by hand.
Migration path that keeps both fast reads and analytics workable. Do not synchronously dual-write to both the serving store and the warehouse; that couples the two systems' availability and adds write latency to the serving path for no serving-path benefit. Instead, stream every change out of the serving store (DynamoDB Streams, or MongoDB Change Streams) asynchronously into the warehouse. The serving path stays fast and simple; the warehouse eventually reflects every change (typically within seconds), which is exactly what "occasional complex analytics" can tolerate.
Worked example
The instinct at "100 million users" is to assume enormous QPS (queries per second). Working the number through from a realistic daily-active fraction shows the opposite, and shows why the cache-aside layer does most of the real work.
# profile-read QPS and cost at 100M registered users.
users = 100_000_000
daily_active_fraction = 0.06 # 6% DAU/registered ratio, a reasonable consumer-app assumption
dau = users * daily_active_fraction
views_per_session = 4 # own profile + a few others viewed per session
daily_reads = dau * views_per_session
avg_qps = daily_reads / 86400
peak_concentration = 9 # traffic is not uniform across 24h; a peak-hour multiplier
peak_qps = avg_qps * peak_concentration
cache_hit_rate = 0.85
peak_origin_qps = peak_qps * (1 - cache_hit_rate)
print(f"DAU = {dau:,.0f}")
print(f"avg qps (all reads) = {avg_qps:,.0f}")
print(f"peak-hour qps (all reads) = {peak_qps:,.0f}")
print(f"peak qps actually reaching the document store after {cache_hit_rate:.0%} cache hit rate = {peak_origin_qps:,.0f}")
# DAU = 6,000,000
# avg qps (all reads) = 278
# peak-hour qps (all reads) = 2,500
# peak qps actually reaching the document store after 85% cache hit rate = 375
Two hundred and seventy-eight average, 2,500 peak, 375 peak at the origin store after caching: at 100 million registered users, that is a modest number for a document store built to handle tens of thousands of QPS per table. The real engineering problem this design has to solve is not raw throughput, it is p99 (99th-percentile) latency per request and cache-invalidation correctness on writes, and both of those are solved by the read-your-writes rule above, not by adding capacity.
Trade-offs & pitfalls
- Assuming "100 million users" implies enormous sustained load. It usually does not; work the DAU and session-shape assumptions through explicitly before sizing anything, and be ready to defend the assumptions, not the headline user count.
- Letting the analytics requirement pull the serving-path schema toward a relational, join-friendly shape "just in case." That is optimizing the 99% of traffic (serving) for the 1% of traffic (analytics), and it is exactly backwards; let the CDC pipeline and the warehouse absorb the analytical shape instead.
- Synchronous dual-writing to the warehouse. It seems simpler than a CDC pipeline but couples serving-path write latency and availability to the warehouse's, which defeats the entire point of separating the two workloads.
- Skipping the read-your-writes rule to save cost. It is tempting to serve every read eventually consistent for the lower price, but "I just saved my profile and it looks unsaved" is a support ticket generator; pay the strong-read cost only on the specific post-write read, not globally.
- Unbounded arrays embedded in the profile document (an ever-growing activity log, for example) quietly turn a cheap single-partition read into an expensive, growing one over a user's lifetime. Keep growth-shaped data in its own store from day one.
Write a Bash one-liner or a short Python script (specify which language you choose) that lists the top 5 processes by memory usage on a Linux server, outputting PID, user, RSS memory in MB, and command. This script will be used as a quick diagnostic during on-call shifts; state any assumptions about the environment.
Sample Answer
Direct answer
Bash, since it needs nothing beyond what every Linux box already has installed:
ps -eo pid,user,rss,comm --sort=-rss | head -n 6 | \
awk 'NR==1{printf "%-8s %-12s %10s %s\n","PID","USER","RSS(MB)","CMD"; next}
{printf "%-8s %-12s %10.1f %s\n",$1,$2,$3/1024,$4}'
ps -eo pid,user,rss,comm lists every process with exactly the four fields asked for; --sort=-rss
orders by resident memory descending; head -n 6 keeps the header plus the top 5; the awk
pass reformats RSS from kilobytes (what ps reports natively) into megabytes and aligns columns
for fast eyeballing during an incident.
Structured elaboration
What RSS actually measures, and why it is the right column here
RSS (resident set size) is how much physical RAM a process is currently occupying, as opposed to
its virtual size (VSZ), which includes memory it has merely reserved or memory-mapped but may
never touch. For "what is eating memory on this box right now," RSS is the relevant number;
VSZ routinely reads far larger and is not comparable across processes the way RSS is.
Assumptions this quick diagnostic makes
procps(which providesps, and the GNU--sortflag) is installed; it is on essentially
every mainstream distribution by default, but a minimal container image can lack it.- The command is run with enough privilege to see other users' processes; an unprivileged user's
ps -eo ...on a hardened system withhidepidset on/procmay only show their own
processes, silently narrowing "top 5 on the server" to "top 5 of mine." - RSS double-counts shared memory: two processes that both map the same shared library each
report that library's pages in their own RSS, so summing RSS across processes overstates true
physical usage. For a single "who is biggest right now" snapshot during an incident this does
not matter; for capacity planning across a whole process tree,PSS(proportional set size,
from/proc/<pid>/smaps_rollup) divides shared pages fairly and is the more honest number. - A single snapshot; if the top consumer is a short-lived burst, this catches it only if it is
still running at the moment you run the command.
Worked example
Run in a live container to prove the exact format and sort order:
$ ps -eo pid,user,rss,comm --sort=-rss | head -n 6 | awk '...'
PID USER RSS(MB) CMD
208 root 3.5 ps
1 root 3.5 bash
210 root 1.5 awk
205 root 1.3 sleep
209 root 1.2 head
On a real application server the same command surfaces the actual memory hog, for example a
leaking worker process climbing steadily across repeated runs, which is the on-call signal this
is built for: run it once to see the current top 5, run it again a minute later, and a process
whose RSS keeps climbing between the two runs is the leak candidate, not just the biggest number
in one snapshot.
Trade-offs and pitfalls
commtruncates to the process's short name (15 characters on Linux) and drops arguments; use
argsorcmdinstead ofcommin the-eofield list if you need to distinguish, say, three
differentpython3workers by their script name or flags.- A one-shot snapshot cannot show a growth rate; if the question during the incident is "is
this leaking or just big," run the same command twice a minute apart, or reach fortop -b -n 1in a loop, rather than trusting a single reading. - Summing the RSS column to estimate "total memory in use" overstates reality because of shared
pages (see PSS above); do not report that sum as the host's actual memory pressure, compare
againstMemAvailablein/proc/meminfoinstead. - If the goal shifts from "quick eyeball during an incident" to "durable metric for
dashboards/alerting," a one-offpscommand is the wrong tool; that belongs in something like
node_exporter scraping/proccontinuously, not a manually re-run diagnostic.
A performance regression in production quietly drove up your cloud bill for several days before anyone noticed. Walk through how you'd handle it end to end: how you'd catch it sooner next time, how you'd contain the damage, find the root cause, quantify what it actually cost the company, and what you'd change so it can't happen again.
Sample Answer
Direct answer
Work it as a full post-mortem in five parts: tighten detection with a cost-efficiency signal that the usual latency and error alerts miss, contain the damage by rolling back or flagging off the change immediately once found, find root cause by correlating the cost curve against the deploy timeline, quantify the actual incremental spend against a clean baseline, and close with both a technical guardrail and a process change so a regression like this cannot run silently again.
Structured elaboration
Catch it sooner: the reason this ran for days is that ordinary monitoring, error rate and latency SLOs (service-level objectives, the internal targets a team holds itself to), did not trip, because the regression was slow-but-not-failing, for example a cache-miss regression, an N+1 query pattern, or a retry loop, that burns several times the compute per request without ever breaching a latency threshold or throwing errors. The fix is to add a cost-per-request or cost-per-unit-of-work metric to the same alerting path as latency and errors, checked on the same daily cadence, not discovered at the end of the billing month.
Contain the damage: once flagged, the fastest containment is almost never "optimize the code," it is "undo the change": roll back the deploy, flip a feature flag, or scale back down to the pre-incident instance count if autoscaling has been quietly masking the regression by buying more capacity. Stop the bleeding before starting root-cause work.
Find the root cause: correlate the cost-rate time series against the deploy timeline, a released commit, a config change, or a third-party dependency update, to find the exact change. Common causes for this shape of incident are a cache configuration regression, a query that lost an index or partition filter, a retry loop with no backoff, or an autoscaling policy that scales out to paper over a symptom instead of the underlying inefficiency being fixed.
Quantify what it actually cost: define a clean baseline spend rate from before the regression, then sum the excess spend rate over the incident window. Shown with real numbers below.
Prevent recurrence: technically, add the cost-per-request metric to the same release-health dashboard that already gates a rollback on latency or error regressions, so a cost regression trips the same automated response. As a process change, require a "touches a hot path or scan pattern" tag on changes that go through this kind of review, and add a short canary period that compares cost-rate, not just error rate, before a rollout is considered complete.
Worked example
Assume baseline daily compute spend was about $850/day before the regression shipped, and it rose to about $2,100/day once the change landed, running for 6 days before being caught. The incremental cost is (2,100 - 850) x 6 = $7,500, computed directly from the stated baseline and observed daily rate over the incident window.
Trade-offs and pitfalls
Rolling back fast is the right containment move, but doing it with zero diagnostic capture, no logs, no profiling snapshot taken during the incident, can mean the same bug quietly reappears in a future release; a brief capture step before or during rollback pays for itself later. Blaming "the deploy" purely from a timeline eyeball, without confirming causation against a coincident traffic increase, risks reverting a legitimate feature and losing track of the real cause; confirm with a canary or before/after comparison, not just a calendar match. A post-mortem that ends at "we now alert on cost-per-request," with no process change such as a review checklist or canary gate, tends to let the same class of change repeat. Be precise when quoting the dollar figure externally: state the baseline and the method, since an imprecise or rounded number that overstates the cost can create a false narrative about what actually happened.
A security or compliance team has the authority to block your work, and initially does, over something they think is too risky. How do you work with them to get to yes without cutting corners?
Sample Answer
Direct answer
When a security or compliance team has the authority to block work and uses it, the goal isn't to overpower them, it's to give them a way to say yes that they would defend to their own leadership. That means understanding the actual concern, proposing controls that address it directly, and building a record that makes the eventual approval easy to justify upward, rather than skipping the concern to hit a deadline.
Structured elaboration
1. Understand the veto, not just the outcome
Ask what specifically drives the block: a known threat pattern, a regulatory obligation, a past incident. A block framed as 'this is too risky' usually decomposes into something concrete once you ask what evidence would change their mind.
2. Propose compensating controls, not blanket reassurance
Bring specific mitigations that map to the stated concern: scoped access, monitoring, a rollback plan, data masking, a smaller blast radius. 'Trust me' rarely moves a team whose job is to not just trust people; a control they can point to in an audit does.
3. Phase the ask so risk and trust build together
Instead of asking for full approval up front, propose a smaller, monitored first step, then expand once it holds up. This gives the blocking team evidence rather than a promise, and it gives you a faster initial yes.
4. When you need executives to sponsor it, not just the compliance team to approve it
Sometimes getting to yes isn't about convincing the blocking team at all, it's about persuading senior executives, without formal authority over them, to sponsor a security or compliance investment that trades short-term revenue for long-term risk reduction. That's a different move: build the case in terms an executive already weighs (the cost of the exposure versus the cost and timeline of the fix), find a credible sponsor who already has their ear, and time the ask to a moment they're already thinking about risk, such as a renewal, an audit, or a near-miss. State the trade-off plainly rather than downplaying either the revenue impact or the risk.
5. When the conflict runs the other direction
The pressure isn't always compliance blocking a launch. Sometimes compliance demands collecting more data for audit purposes, and that request conflicts with the team's own privacy commitments to users. Handle this the same way: scope exactly what the audit requirement needs, then look for a way to satisfy it without violating the privacy commitment, such as aggregating instead of storing per-user data, sampling instead of full capture, or purpose-limited access with automatic expiry. If a genuine conflict remains after that, escalate it as a policy conflict for someone empowered to decide between the two obligations, rather than either side unilaterally overriding the other.
Worked example
A security team initially blocks a new integration on a financial product, citing customer-data exposure risk. Working sessions with security and the app owner map the specific risk to two things: a broad data scope and no kill switch. The team proposes scoped test accounts, data masking, and a remote kill switch, then agrees to a phased rollout: verify the low-risk paths first, escalate to the higher-risk ones only after the first phase holds up under monitoring. Security signs off on the phased plan. Separately, when the same team later wants to expand data collection to satisfy a new audit requirement, they find that a sampled, time-limited collection window satisfies the auditors just as well as full, indefinite collection, so the privacy commitment to users doesn't have to give.
Trade-offs and pitfalls
- Working around a block quietly (shipping a smaller version without telling the blocking team) buys short-term speed and damages the relationship you will need next time; always close the loop even when you find a narrower path.
- Compensating controls that never get revisited become permanent scaffolding; agree upfront on when the phased approach graduates to full trust, not just how it starts.
- On the upward-influence path, leading with fear rather than a clear trade-off tends to get budget approved once and then quietly deprioritized later, because the executive never actually weighed the cost against the risk. Naming the trade-off explicitly is what makes the commitment durable.
- Overriding a genuine policy conflict (audit needs versus privacy commitments) unilaterally, instead of escalating it, tends to resurface as a bigger trust problem with users or regulators later than the original block would have cost in time.
You are given a function that has grown to do five unrelated things (for example: parsing input, validating it, running business rules, persisting results, and sending notifications) in a single 400+ line block. Walk through how you would decompose it into small, well-named, independently testable pieces, and what you would check before and after to confirm you did not change behavior.
Sample Answer
Direct answer. Decompose by responsibility, not by line count: pull out one function per distinct concern (parse, validate, compute, persist, notify), give each a name that states its single job, and compose them in a thin orchestrator that reads like the original steps.
Before (one function, five jobs)
def handle_order(raw):
order_id = raw["id"]; email = raw["email"]; items = raw["items"] # parse
if not items: raise ValueError("order has no items") # validate
if "@" not in email: raise ValueError("bad email")
total = sum(i["price"] * i["qty"] for i in items) # compute
if total > 100: total *= 0.9
DB[order_id] = {"email": email, "total": total} # persist
OUTBOX.append(f"receipt to {email}: ${total:.2f}") # notify
return total
After
def parse_order(raw: dict) -> dict:
return {"id": raw["id"], "email": raw["email"], "items": raw["items"]}
def validate_order(order: dict) -> None:
if not order["items"]: raise ValueError("order has no items")
if "@" not in order["email"]: raise ValueError("bad email")
def compute_total(items, *, bulk_discount_threshold=100, bulk_discount_rate=0.9) -> float:
total = sum(i["price"] * i["qty"] for i in items)
return total * bulk_discount_rate if total > bulk_discount_threshold else total
def save_order(db, order_id, email, total) -> None:
db[order_id] = {"email": email, "total": total}
def send_receipt(outbox, email, total) -> None:
outbox.append(f"receipt to {email}: ${total:.2f}")
def handle_order_v2(raw, db, outbox) -> float:
order = parse_order(raw)
validate_order(order)
total = compute_total(order["items"])
save_order(db, order["id"], order["email"], total)
send_receipt(outbox, order["email"], total)
return total
Verified against two cases: a two-item order totaling 120 (discounted to 108.0) and a single-item order totaling 20 (no discount) -- both versions return identical totals.
How to confirm you didn't change behavior
- Run the OLD function and the NEW orchestrator against the same set of inputs (including edge cases: empty items, exactly-at-threshold totals, malformed email) and diff the outputs.
- If there's an existing test suite, run it unchanged against the new code first -- a passing suite that never exercised the discount boundary is itself a gap worth flagging, not just a green check mark.
- Where no tests exist yet, write characterization tests against the OLD function first, then refactor, then confirm the same tests still pass -- this way the safety net exists before you touch anything.
Trade-offs and pitfalls
compute_total's discount threshold is now a named, testable, overridable parameter instead of a buried magic number -- a deliberate improvement, but note it as a design decision so a reviewer knows it wasn't accidental scope creep.- Over-decomposing (a function per line) makes you trace five files to understand one flow; stop splitting when each piece maps to one clear reason to change.
- The orchestrator itself now has a job: sequencing. Keep it free of business logic so it doesn't quietly become a sixth responsibility.
How do the stack and the heap differ from the kernel's point of view: how does each region grow, how are guard pages and limits used, and why does a stack overflow typically kill the process with a fault while heap exhaustion shows up differently?
Sample Answer
The kernel's view. To the kernel a process's memory is a set of VMAs (virtual memory areas, each a start, an end and permissions). The stack and the heap are just two VMAs with different growth rules; nothing in the hardware says "stack".
Stack. At execve (the system call that starts a new program in a process) the kernel builds the initial stack near the top of user space (with argv, the environment variables, and the auxiliary vector, a table of facts the kernel passes to the program such as the page size) and marks the VMA as growing downward. When code pushes below the current bottom, the access faults, the kernel sees the address is just under a grow-down VMA and within the limit, and extends the VMA, which is how a stack grows without anyone asking. The limit is RLIMIT_STACK (a per-process resource limit that caps how large the main thread's stack may grow). The demo below printed 8192 KiB (8 MiB) in its container, the usual default. Beyond the limit, or into the kernel's protected gap, the fault becomes a SIGSEGV. That gap is stack_guard_gap, 256 pages (1 MiB with 4 KiB pages) by default in the kernel source mm/mmap.c (the stack_guard_gap= boot parameter changes it): the kernel refuses to let another mapping sit directly beneath the main stack, so a runaway stack cannot quietly grow into a neighbour. That is the guard for the one stack that grows on demand. Other threads' stacks work differently: they are fixed-size ordinary mmap regions that the C library allocates, and they do not grow, so the library puts a guard page (a page with no access rights) at the low end; touching it faults. So the two guards differ in who makes them and in which stack they protect: a kernel-enforced 1 MiB gap under the growable main stack, and a library-made one-page no-access region at the end of each fixed thread stack.
Heap. The classic heap is the program break (brk, the end of the process's data segment), which grows upward; larger or later allocations come from separate anonymous mmap regions. The C library's allocator (malloc) chooses between them, and its internals are a language-memory topic. The kernel's part is simply to add address ranges on request and to back them with frames lazily on first touch.
Why the failures look different. Stack growth is implicit: the program never asks, the access just lands in unmapped space, so exhaustion is a fault in the middle of a normal instruction. The kernel kills the process with SIGSEGV (a handler cannot run on the overflowed stack, so a program that wants to report the crash installs an alternate signal stack with sigaltstack, a separate small memory area the kernel switches to when running a signal handler). Heap growth is explicit: the program asks via malloc, which calls brk or mmap, and a refusal comes back as an error code (malloc returns NULL and errno, the C library's error-code variable, is set to ENOMEM, "out of memory"), which code can handle. The real exception is overcommit: the kernel often grants more memory than physically exists (it promises address space and only backs pages when they are touched), so it says yes and runs out of RAM later, and the failure becomes the OOM killer (the kernel's last resort that picks a process and kills it) sending SIGKILL when pages are touched, which no code can intercept.
Demo (Linux aarch64 container, built gcc -O0 -w stack.c -o stack):
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
static void on_segv(int sig) {
static const char m[] = "SIGSEGV: stack limit reached (handler runs on alternate stack)\n";
(void)sig; write(1, m, sizeof m - 1); _exit(0);
}
static volatile char sink;
static long depth;
static void recurse(void) {
volatile char buf[1024];
buf[0] = 1; sink = buf[0];
depth++;
recurse();
sink = buf[0];
}
int main(void) {
struct rlimit rl;
setvbuf(stdout, NULL, _IONBF, 0);
getrlimit(RLIMIT_STACK, &rl);
printf("stack limit: %lu KiB\n", (unsigned long)rl.rlim_cur / 1024);
/* heap: cap address space, then ask for more than the cap */
struct rlimit as = { 256UL << 20, 256UL << 20 };
setrlimit(RLIMIT_AS, &as);
void *p = malloc(512UL << 20);
printf("malloc(512 MiB) under 256 MiB RLIMIT_AS: %s\n", p ? "ok" : "NULL (ENOMEM), program keeps running");
static char altstack[16384];
stack_t ss = { altstack, 0, sizeof altstack };
sigaltstack(&ss, NULL);
struct sigaction sa; memset(&sa, 0, sizeof sa);
sa.sa_handler = on_segv; sa.sa_flags = SA_ONSTACK;
sigaction(SIGSEGV, &sa, NULL);
recurse();
return 0;
}
stack limit: 8192 KiB
malloc(512 MiB) under 256 MiB RLIMIT_AS: NULL (ENOMEM), program keeps running
SIGSEGV: stack limit reached (handler runs on alternate stack)
Reading the demo: in recurse, volatile tells the compiler the stores to buf and sink must really happen, so it cannot optimise the recursion into a loop or drop the 1 KiB array; each call therefore consumes at least 1 KiB of stack, and 8 MiB runs out after at most about 8,000 calls. In main, setrlimit(RLIMIT_AS, ...) caps the total address space at 256 MiB (RLIMIT_AS is the resource limit on address-space size), so the following 512 MiB malloc must fail with NULL. For the handler: altstack is a static 16 KiB array, stack_t ss describes it to sigaltstack (base pointer, flags 0, size), and sigaction with SA_ONSTACK registers on_segv and tells the kernel to run it on that alternate stack instead of the exhausted one; without it the handler could not even start.
In this demo the recursion stops at the 8 MiB RLIMIT_STACK limit; the guard gap and thread guard pages described above give the same SIGSEGV when a stack runs into them. Removing SA_ONSTACK makes the program die with a plain segmentation fault (exit status 139) instead of printing the message. Heap exhaustion became a catchable NULL; unbounded recursion became a SIGSEGV that is only reportable because of the alternate stack.
Practical consequences. Large arrays belong on the heap, not in a local variable, and deep recursion should be bounded or turned into a loop; ulimit -s raises the main-thread limit but thread stack size is set when the thread is created (pthread_attr_setstacksize). To diagnose a stack overflow, look at the repeating frames in the backtrace and the crash address just below the stack VMA in /proc/<pid>/maps.
How do you record SLOs and error-budget policies for a customer-facing API in its architecture documentation? Give a concrete example for a checkout success rate.
Sample Answer
Direct answer
Record each SLO as a small structured entry: the SLI (how we measure), the target, the window, what counts as good and bad events, the data source, the owner, and an error-budget policy saying what the team does as the budget is consumed. Put it in the reliability section of the architecture document, next to the diagram of the components it measures, and link it to the alerts and the runbook.
Definitions (one line each)
- SLI (service level indicator): a measured ratio, good events divided by valid events.
- SLO (service level objective): the internal target for that ratio over a window.
- SLA (service level agreement): a contractual promise to customers, with consequences (credits, penalties). It should be looser than the SLO so the team is warned before breaching it.
- Error budget: the allowed amount of failure, which is 100% minus the SLO.
Concrete example: checkout success rate
SLI: successful checkouts / valid checkout attempts
Good: order confirmed and payment authorized
Valid: all attempts except client-caused failures (card declined, invalid input)
Target: 99.9% over a rolling 28 days
Source: request logs at the API gateway
Owner: Payments team
Excluding card declines matters: a declined card is the system working correctly, and counting it would let customer behaviour, not service health, drive the number.
Budget arithmetic. Assume 12,000,000 valid attempts in 28 days (illustrative volume).
12,000,000×(1−0.999)=12,000
So 12,000 failed checkouts are allowed in the window. If 3,000 have failed by day 7, the budget used is 3,000 / 12,000 = 25%, and the window elapsed is 7 / 28 = 25%: exactly on pace. If the error rate is a sustained 0.5% against an allowed 0.1%, the burn rate (observed error rate divided by allowed rate) is 5, and a full budget is spent in 28 / 5 = 5.6 days.
Error-budget policy (what changes at each level)
| Budget used | Action |
|---|---|
| Below 50% | Normal feature delivery |
| 50% to 100% | Reliability work is prioritized in planning; risky releases need extra review |
| Over 100% | Feature releases pause except fixes; a postmortem (blameless written review of what happened) is required; resume when back within budget |
Measurement windows and remediation. A rolling 28-day window is smooth; a calendar-month window resets and can hide a bad week. Example: if checkouts fail heavily from the 24th to the 30th, a calendar-month SLO shows a fresh, full budget on the 1st, while a rolling window still counts those days until they slide out 28 days later. In the rolling window each day, the oldest day drops out and the newest is added, so the number moves gradually. Add a fast-burn alert that pages (automatically alerts the on-call engineer, night or day) and a slow-burn alert that opens a ticket. With the numbers above (12,000,000 attempts over 28 days is about 17,900 per hour, and a 28-day window is 672 hours):
- Fast burn: burn rate above 14 for 1 hour. That is an error rate above 14 x 0.1% = 1.4%, about 250 failed checkouts in the hour, and it uses 14 / 672, about 2%, of the whole budget in one hour. At that pace the budget is gone in 2 days, so a human must look now.
- Slow burn: burn rate above 3 for 24 hours. That is an error rate above 0.3%, about 1,290 failures per day (using about 428,600 attempts per day), and about 10.7% of the budget (3 / 28). Nothing is on fire, but the trend needs a ticket.
Both thresholds are illustrative; teams choose them from how much budget they accept losing before someone reacts. "Valid events" are the attempts that count (client-caused failures such as declined cards are removed), and "good events" are the valid ones that succeeded. Link a remediation playbook per alert (a short step-by-step guide, like a runbook): what to check first, how to roll back, when to escalate.
The 50 percent and 100 percent levels are conventions, not laws: 50 percent is an early warning with half the budget and roughly half the window left to react, and 100 percent is the point where the promise is broken. Teams tune them.
Pitfalls: SLO higher than the dependencies can deliver; counting invalid events; no policy consequences, so the number is decorative; the SLA set equal to the SLO.
Recommended Additional Resources
- Site Reliability Engineering: How Google Runs Production Systems (The SRE Book) - foundational reading on SRE philosophy, principles, and practices
- The Site Reliability Workbook: Practical Ways to Implement SRE - practical applications of SRE concepts with case studies
- Designing Data-Intensive Applications by Martin Kleppmann - excellent reference for distributed systems, scalability, and system design
- LeetCode Premium - practice algorithms and data structures with problems organized by topic, difficulty, and company tags
- HackerRank - coding challenges, interview preparation, and system design practice problems
- Cracking the Coding Interview by Gayle Laakmann McDowell - comprehensive guide to technical interview preparation
- System Design Interview by Alex Xu and Kai Shiyu - system design fundamentals and common interview patterns and solutions
- Google Cloud Platform documentation - familiarize yourself with Google's infrastructure services and architecture
- Prometheus documentation - monitoring and alerting, core tools in modern SRE
- Linux man pages and tutorials - develop practical Linux system administration and troubleshooting skills
- Google Site Reliability Engineering Blog - real SRE thinking and practices from Google engineers
- Networking fundamentals - TCP/IP protocol, DNS, HTTP, and network troubleshooting
Search Results
Top 25 SRE Interview Questions and Answers for 2025 - YouTube
Want to crack your SRE (Site Reliability Engineer) interview fast? This video covers the most commonly asked SRE interview questions and answers, ...
Google Site Reliability Engineer (SRE) | Interview Experiences - Blind
Hi everyone, I have a 45 minute coding interview for a Software Engineer III, Site Reliability Engineering position at Google coming up and I'm not sure ...
Visa Site Reliability Engineer Interview Questions - Exponent
Review this list of Visa site reliability engineer interview questions and answers verified by hiring managers and candidates.
Top Google Interview Questions for Software Engineers (2024)
The Google interview questions for software engineers are focused on three topics: algorithms, data structures, and distributed system design.
Top Toughest Google Interview Questions To Prepare
Majorly, an interviewer or a hiring manager would ask questions about coding problems, system design, behavioral questions, and more. However, technical ...
Tech Interview Preparation Articles - IGotAnOffer
Google Site Reliability Engineer (SRE) Interview (questions, process, prep). Complete guide to the Google site reliability engineer interview. Understand ...
This interview preparation guide was generated using AI-powered research from the sources listed above. While we strive for accuracy, we recommend verifying critical information from official company sources.
Want to create your own tailored preparation guide using our deep research?
Get Started for FreeInterview-Ready Courses
Visual-first, interactive, structured learning paths
Browse Site Reliability Engineer (SRE) jobs
AI-enriched listings across hundreds of company career pages
Explore Jobs