← Grammarly Interview Insights

Grammarly·Software Engineer·Onsite - System Design / Architecture·Senior

SeniorPrefer not to say
May 2026

Summary

Grammarly system design round for a software engineer role. The problem was building a transactional key-value store from scratch in memory, which sounds manageable until you start thinking about nested transactions and how count() is supposed to behave mid-transaction.

Questions Asked (1)

Q1

Design an in-memory key-value store that supports transactions, including set, get, count, begin, commit, and rollback operations. Transactions can be nested, and count must reflect the current visible state including uncommitted changes.

System DesignAlgorithms & Data StructuresTechnical Trade-offs
Author's notes

My first instinct was a simple hashmap and I got maybe two minutes into explaining it before they asked about nesting.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying requirements and constraints, then propose a design using a stack of transaction layers, each with its own local store and a reference to its parent. Explain how operations like set, get, and count traverse the stack to respect visibility, and how commit and rollback manipulate the stack. Discuss trade-offs between simplicity and efficiency, and consider edge cases like nested rollbacks and count performance.

Pro tip: Demonstrate awareness of real-world systems by mentioning that count can be optimized with a global counter adjusted on commit, but be careful with rollbacks. Also, explicitly state that get should check the current transaction first, then walk up the stack, to show you understand visibility semantics.

1. Clarify requirements and constraints

Ask about expected scale, concurrency, persistence, and whether count should be O(1) or can be O(n). Confirm that nested transactions are supported and that uncommitted changes are visible within the transaction.

2. Design core data structures

Propose a stack of transaction contexts, each containing a map for local changes and a pointer to the parent. The base context holds committed data. For count, maintain a global count of committed keys and adjust per transaction.

3. Define operation semantics

Explain how set, get, and count work: set writes to the current transaction's map; get checks current map then walks up; count sums committed count plus unique keys in active transactions. Describe begin (push new context), commit (merge into parent), and rollback (discard current context).

4. Analyze trade-offs and optimizations

Discuss time/space complexity: get is O(depth) worst-case, count can be O(total keys) if naive. Suggest optimizations like maintaining a per-transaction count delta or a global counter with careful rollback handling.

5. Address edge cases and testing

Cover scenarios like nested rollbacks, committing an empty transaction, and concurrent access (if applicable). Outline a testing strategy including unit tests for each operation and integration tests for nested transactions.

Key Points to Mention

  • Use a stack of transaction layers with parent pointers to support nesting.
  • Ensure get and count respect visibility: check current transaction first, then ancestors.
  • Commit merges local changes into parent; rollback discards current layer.
  • Count must include uncommitted changes, so it cannot rely solely on committed data.
  • Discuss time complexity: get is O(depth), count can be optimized with deltas.
  • Consider thread-safety if the store is to be used concurrently.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.