Design an Enterprise System
- Design a multi-tenant system of record in order, starting with tenant isolation and authorization
- Choose a tenant isolation model, scope roles to tenants, and keep history with effective dates
- Keep fan-out writes consistent, contain noisy tenants, and keep an append-only audit trail
Understanding the prompt
"Design an enterprise system" is deliberately broad. The first mistake is treating it as a consumer-scale prompt in disguise and sizing a cache tier for a billion-user feed. Enterprise software works differently. One tenant is a whole company, and tenants number in the thousands, nowhere near billions. The value lives in data that is correct, private, and auditable. Surviving a viral spike matters little. So before naming a class or drawing a box, decide what kind of system you're building. It's a system of record that many separate organizations use through one shared codebase. Each sees only its own world and configures that world differently. Each expects a number entered today and a number from three years ago to be retrievable and trusted.
Build the general shape first and layer the company flavor on afterward. A candidate who can only recite a Workday-specific answer but cannot design a multi-tenant SaaS from scratch fails the moment the interviewer swaps the domain from payroll to something else. So the plan here is two passes. The first pass is the reusable framework that holds for any enterprise SaaS. Isolate tenants and gate every request through identity and authorization. Model a core domain service, and choose storage and a consistency posture. Move the slow work off the request path. The second pass adds what makes enterprise software its own animal, and where Workday sits inside it. That means tenant configurability, effective-dated history, audit and compliance, and the integration surface every large customer demands.
Pinning the requirements first
State the functional surface plainly so the design has edges. Many independent organizations, the tenants, share one running system. A user signs in and can only ever act inside their own tenant. Inside that tenant they read and write business objects, the domain records the product is about, and those objects have a shape the tenant itself gets to configure. Every change is attributed to a person and stamped with when it happens and when it takes effect. Slow work, a payroll run, a headcount rollup, a report over the whole population, runs in the background and must not block the interactive request. That is the core, and it is enough to build something real without drifting into a feature tour.
Then name the non-functional bar, because in enterprise software it shapes the whole design. Tenant isolation has to be airtight: a query that forgets its tenant filter and returns another company's salaries is not a bug, it is a breach that ends a contract. Authorization is per-tenant and role-based, so the same person can be an administrator in one context and a plain viewer in another. Data is durable and consistent for records that are money and law, and history is never destructively overwritten. And the system stays predictable under a noisy neighbor, one tenant kicking off a huge job cannot starve everyone else. Say which of these you will treat as hard invariants, and you have set the frame the rest of the design defends.
- What the round grades: model the business. Clean entities and relationships, honest isolation, and correct history. Not requests-per-second math. If you find yourself sharding a cache before you have named the domain objects, you have misread an enterprise prompt.
- A tenant is a whole company: thousands, not billions. Scale is measured in tenants and in objects per tenant. There's no global user firehose. That shifts the hard problems from raw throughput to isolation, configurability, and correctness of the data each tenant owns.
- Isolation is an invariant: never cross tenants. Every read and write carries a tenant and is filtered by it. A missing tenant filter that leaks one company's data into another is the worst failure this kind of system can have. Design for it from the first line instead of a later hardening pass.
- History is the product: keep the past, dated. Records are effective-dated: a change knows when it takes effect and the prior value is never erased. Payroll and headcount must be reconstructable as of any past date, so the store versions records rather than overwriting them in place.
The reusable framework
Any multi-tenant enterprise SaaS comes down to a small stack of decisions, made in a fixed order because each one constrains the next. Isolation comes first because it colors the schema, the queries, and the deploy model. Identity and authorization come next because nothing else is allowed to run until a request is bound to a tenant and a set of permissions. Only then does the domain service matter, and only after that do storage, consistency, and the slow background work. Walk the interviewer down this ladder and the design stays coherent no matter which domain they hand you.
Tenant isolation
Decide how one running system keeps thousands of companies apart: a shared schema with a tenant key on every row, a schema per tenant, or a database per tenant. This choice sets the blast radius of a bug and the cost of onboarding a new customer, so it is made first and everything downstream inherits it.
Identity and authorization
Put a gateway in front that authenticates the user, resolves which tenant they are acting in, and checks a role-based permission on the specific resource before any handler runs. Authorization is per-tenant, so the same person carries different powers in different contexts.
The business-object service
The core domain service owns reads and writes of business objects. In enterprise software, a tenant sets the shape of an object as configuration. No developer hard-codes the columns. So the service validates against a per-tenant model instead of a fixed schema.
Storage and consistency
Pick a durable transactional store for the objects, keep writes strongly consistent because they are money and law, and version records by effective date so history survives. Reporting reads a separate store so a heavy analytical query never contends with interactive writes.
Background jobs and reporting
Move slow, fan-out work, a payroll run, a headcount rollup, a population report, off the request path onto a queue and idempotent workers. The interactive write returns fast and emits an event; the heavy computation happens asynchronously and lands in the reporting store.
Scale and failure
Scale is per-tenant, so the work is about fairness and blast radius. Isolate a noisy tenant so one customer's giant job can't starve the rest, and cap work per tenant. Make every background step retry-safe, so a crash mid-run doesn't double-pay anyone.
Decision one: tenant isolation
Isolation is the decision the whole design is built on, so make it explicitly and name the trade-off. There are three common models. A shared schema keeps every tenant's rows in the same tables with a tenant_id column on each, and every query filters by it. With schema-per-tenant, each tenant gets its own set of tables inside one database. Database-per-tenant gives each its own database entirely. They trade along the same axis in opposite directions. The more you share, the cheaper it is to run and the easier to onboard a new tenant. The wall between customers gets thinner, though, and one query that forgets its filter costs more. Separate more, and the wall gets stronger. A per-tenant restore or a regional data-residency requirement gets simpler. You carry more operational weight per customer in return.
With thousands of tenants, many of them small to medium, the shared-schema model is the usual default. A schema or database per tenant, multiplied by thousands, is an operational burden no one wants to carry. But sharing puts the entire isolation guarantee on one fragile thing: the tenant filter on every query. The design has to make that filter impossible to forget. Don't rely on every developer remembering it. Push the tenant into the connection or session as a context the data layer injects automatically. Then a handler physically can't issue an unscoped query. Postgres row-level security is one concrete way to enforce it at the database instead of in application code. Even a hand-written query gets filtered by the tenant on the session. State that mechanism out loud. "We filter by tenant_id", with no word on how you stop a forgotten filter, is the answer that leaks data in production.
- Shared schema, tenant key: cheapest to run. One set of tables, a tenant_id on every row, a filter on every query. Onboarding a tenant means adding a row, with no migration. The whole isolation guarantee rests on the filter, so it must be enforced by the data layer or row-level security, never left to developer discipline.
- Schema per tenant: a middle ground. Each tenant gets its own tables in one database. A stronger wall than a shared key and per-tenant backup is cleaner, but thousands of schemas strain migrations and connection pooling, so it suits fewer, larger tenants.
- Database per tenant: strongest wall. Full separation, the simplest per-tenant restore and the cleanest answer to data-residency and a single tenant's regulatory demands. It costs the most per customer, so it is reserved for the largest or most sensitive accounts, often as a premium tier.
- The onboarding question: how a tenant is born. Interviewers probe provisioning: a new tenant needs its isolation set up, its configuration seeded, and its first admin created. In shared-schema that is inserting rows; in database-per-tenant it is standing up infrastructure. Name which and the cost is honest.
Isolation that holds
- Binds the tenant to the request context at the gateway and lets the data layer inject the filter, so a handler cannot issue an unscoped query
- Enforces the boundary in the database with row-level security, so even a hand-written query is scoped to the session's tenant
- Names the isolation model and its cost on purpose: shared schema for many small tenants, a dedicated database for the few that demand it
- Treats a cross-tenant read as a breach, worse than any bug, and designs the test that proves one tenant can never see another's rows
Isolation that leaks
- Says "we add a tenant_id and filter by it" with no mechanism that stops a developer from forgetting the filter
- Puts the tenant only in application code, so one raw query or one background job runs unscoped and leaks
- Reaches for a database per tenant at thousands of tenants without pricing the operational weight
- Treats onboarding as an afterthought, so a new tenant has no defined path to its config and first administrator
Decision two: identity, authz, and RBAC
Nothing in the system runs until a request is bound to a real user, a specific tenant, and a set of permissions. A gateway sits in front of every service and does three things in order. It authenticates the caller, usually by validating a signed token from the tenant's identity provider. Large enterprises bring their own single sign-on and expect SAML or OIDC federation, with no password stored in your system. Next it resolves which tenant the request acts in, from the token and the route, and pins it into the request context. Then it checks authorization. Does this principal, with these roles, have permission to take this action on this specific resource? The resource itself belongs to a tenant. Only if all three pass does the request reach a domain handler.
Authorization is role-based. Candidates most often skip the fact that permissions are scoped. A person isn't simply an administrator. They're an administrator within one tenant, and often within one part of it. So the check has two layers, and the order matters. The outer layer is the tenant match. Even a perfectly valid role gets denied if the resource belongs to a different tenant. Tenant mismatch is a hard deny before any role math runs. The inner layer resolves the principal's roles to the actions they grant on the object's type. If any role grants the action, it's allowed. Tenant isolation as the outer gate stops a crafted request from using a real role in one tenant to reach a resource in another.
// Authorization resolves a principal's ROLES to a permission on a resource that// is always scoped to one tenant. The tenant check is the OUTER gate: even a// valid role must never reach across tenants, so a tenant mismatch is a hard// deny BEFORE any role math runs.enum Action { VIEW, EDIT, APPROVE }record Principal(String userId, String tenantId, Set<String> roles) {}class AccessPolicy { // For this tenant: role name -> the actions it grants on an object type. private final Map<String, Set<Action>> grants; boolean allows(Principal p, BusinessObject obj, Action action) { // 1. Tenant isolation is checked first and on its own. A resource in // another tenant is denied no matter how privileged the principal is. if (!p.tenantId().equals(obj.tenantId())) return false; // 2. Any one of the principal's roles that grants this action is enough. for (String role : p.roles()) if (grants.getOrDefault(role, Set.of()).contains(action)) return true; return false; // no role granted it: deny by default }}Two properties of this small class are worth saying to the interviewer. It denies by default, so a resource with no matching grant is refused rather than allowed, which is the safe direction for a system holding pay and personal data. And the grants themselves are per-tenant configuration instead of compiled-in constants. One customer's notion of a "Manager" role, and the actions it carries, differs from another's. That configurability of the permission model is the seam that lets the same codebase serve a bank and a hospital, and it is the natural bridge into how the domain objects are themselves configured.
Decision three: the business-object service
The core service owns the domain: it reads and writes the business objects the product is about, a worker, a position, an organization, a pay result. In a consumer app those would be fixed tables with fixed columns. In enterprise software they usually cannot be, because every tenant wants a slightly different shape, its own custom fields, its own required-versus-optional rules, its own object types layered on the standard ones. So the domain service is built around a model where the shape of an object is data the tenant configures. No developer hard-codes the columns. A tenant defines its field set; an object carries its values in a map keyed by those field definitions; the service validates a write against the tenant's model rather than against a rigid schema.
The second thing the domain object carries is time. A business record is more than a single current value. It's a timeline. This worker was grade three from January and grade four from June. If someone made a mistake, a correction to the June value must not erase the fact that grade three was in effect before it. So an object holds a series of versions ordered by the date each takes effect. Reading it means asking for the value in effect as of a given date: the latest version at or before that date. A TreeMap from effective date to version makes that read a single floor lookup. An amendment is just another entry, never an overwrite. Storing versions by effective date, instead of a mutable current row, turns a CRUD store into a system of record.
// A business object is a tenant-scoped record whose SHAPE is configuration, not// code: each tenant defines its own fields, so values live in a map rather than// fixed columns. History is kept BY EFFECTIVE DATE and never overwritten.record FieldDef(String name, String type, boolean required) {}record Version(LocalDate effectiveFrom, Map<String, Object> values) {}class BusinessObject { final String tenantId; // the isolation key, on every object final String id; // stable identity across all versions final String type; // "Worker", "Position", ... per tenant // versions ordered by the date each takes effect private final NavigableMap<LocalDate, Version> timeline = new TreeMap<>(); // The value in effect as of a date is the LATEST version at or before it. Version asOf(LocalDate when) { var entry = timeline.floorEntry(when); return entry == null ? null : entry.getValue(); // null: before it existed } // An amendment is a new dated version, or a correction to an existing date. // The prior value is retained, so the past stays reconstructable. void amend(Version v) { timeline.put(v.effectiveFrom(), v); }}The two modeling choices pay off together. The field set is configuration, so onboarding a customer with an unusual data model is a configuration change. No schema migration or deploy is needed. The object is a timeline, so a follow-up like "what was this worker's grade last March" is a floor lookup. You never lost the value. Both live behind the domain service. The storage layer underneath can stay a plain set of tables while the richness sits in the model. That keeps the persistence decisions in the next section simple.
The architecture end to end
With the three core decisions made, the system is the wiring between them and the path of one request. Draw it. Read the diagram by role. Blue is an actor outside the system, a signed-in user or an admin driving a bulk integration. Violet is a service: the gateway that authenticates and scopes, the authorization service, the domain service, the workers, and the reporting service. Amber is the queue that carries events and jobs off the request path. Green is stored state. That covers the tenant metadata holding config and roles, the tenant-partitioned store of business objects, the append-only audit log, and the separate reporting store. Follow a write from the top. It gets authenticated and scoped, then checked for permission. It lands in the object store as a new dated version and is recorded in the audit log. Then it goes out as an event. The workers pick it up and roll it into the reporting store, where reports read.
Storage, consistency, and background work
Now the storage and consistency posture, which follows from what the data is. The business objects are money and law. Their store is a durable, strongly consistent transactional database, a relational store such as Postgres, partitioned by tenant. Strong consistency here earns its cost. A pay change or an approval has to read back correctly the instant after it commits. Two edits to the same record must not silently clobber each other. So writes are transactional, and optimistic version checks resolve concurrent edits instead of a last-writer-wins race. A consumer feed takes the opposite posture, where eventual consistency is a fine trade for scale. Explain why the trade flips for a system of record. The round looks for that reasoning.
Reporting pulls the other way, so give it its own store. A report over an entire tenant's population, like a headcount rollup or a compensation summary, is a heavy scan. Run against the same tables, it would contend with interactive writes. So the workers keep a separate reporting store, fed from the stream of change events, and reports read from it. That store may lag the write path a little. A summary tolerates being seconds stale in a way a pay calculation doesn't. Accepting that bounded staleness keeps a giant report from slowing down someone editing a record. The slow work runs on idempotent workers behind the queue: the payroll run, the rollups, the imports. The interactive write commits fast and emits an event, and the fan-out happens off the request path. Key the workers by the event. A worker that dies halfway through a payroll run and retries then recomputes instead of double-paying. In this domain, a duplicated side effect is a real person paid twice.
- Object store: strong and transactional. Business objects are pay and legal state, so writes are transactional and read-your-writes consistent, with optimistic version checks to stop concurrent edits from clobbering each other. Eventual consistency, fine for a feed, is the wrong trade here.
- Reporting store: separate, allowed to lag. Analytical scans read a store the workers keep fed from change events, away from the transactional tables. Heavy reports never contend with interactive writes. It tolerates bounded staleness because a summary can be seconds behind and a pay calculation cannot.
- Background work: queue plus idempotent workers. The interactive write commits and emits an event; slow fan-out (a payroll run, a rollup, a bulk import) happens on workers behind a queue. Workers are keyed by the event so a crash-and-retry recomputes instead of double-paying a real person.
- The audit log: append-only, never mutated. Every mutation appends who, what, and when-effective to an immutable log. It is written on the same path as the change so it cannot drift from reality, and never updated or deleted, so the trail itself is trustworthy under a compliance review.
Scale and failure handling
Scale in a multi-tenant system is a fairness problem before it's a throughput problem. Frame it that way. The load comes from thousands of tenants of wildly different sizes. The danger is a noisy neighbor. One large customer launches a huge bulk import or an all-population report and eats enough shared capacity to slow everyone else. The defenses are per-tenant. Rate-limit and quota work per tenant, so one company can't monopolize the request path. Cap the background work a single tenant can have in flight. A giant payroll run then drains at a bounded rate instead of swamping the worker pool. For the largest or most sensitive tenants, go back to the isolation decision from the first section. Give them dedicated capacity, such as a dedicated database or worker pool, so their load never shares a pipe with everyone else's.
Then walk the failure modes out loud. An enterprise buyer's real question is what happens on the bad day. A worker crashes mid payroll run. Every job is idempotent and keyed, so it resumes without paying anyone twice. A dependency is briefly down. Writes to the object store are transactional and either fully commit or fully roll back. No legal record is left half-changed. A bad deploy or a bad import corrupts a tenant's data. The isolation model has to allow a single-tenant restore without touching anyone else. That's a quiet argument for stronger isolation for premium accounts. The worst case is a query that escapes its tenant filter. Enforce the tenant at the database with row-level security, so an unscoped query returns nothing at all. Name these, and tie each to a design choice you already made. That shows you've run enterprise software as well as drawn it.
Layering Workday's flavor
The framework so far is any multi-tenant enterprise SaaS. Now add what makes Workday its own product. The model was built to receive it, because each Workday distinctive is a decision you already made, turned up. Configurability is the business-object service built around a per-tenant field model instead of fixed columns. Push it further, and a tenant configures object types, validation rules, and business-process flows as well as fields, all as data. The same codebase then serves a bank and a university without a fork. Enterprise buyers care about that configuration-versus-customization line. The tenant configures within a model the vendor controls. Nobody forks code the vendor then can't upgrade.
Effective-dating is the timeline in the domain object, taken as seriously as a system of record for pay and headcount demands. A worker's compensation, position, and organization each carry their own dated history. A retroactive correction inserts a version without erasing what was in effect before. Reports can run "as of" any past date by asking each object for its value at that date. Audit and compliance are the append-only log, treated as product instead of plumbing. It records who changed what, when they changed it, and when it takes effect, immutable and queryable. A customer's auditors and regulators demand exactly that trail. Integrations are the bulk-admin actor and the queue, hardened into a first-class surface. No large enterprise runs one system. Inbound and outbound feeds to payroll providers, benefits vendors, and identity systems are a core requirement. Rate-limit them per tenant, so an integration burst never becomes a noisy neighbor.
The runnable core: a tenant-scoped effective-dated store
Interviewers often compress a whole design into one implementable class. For an enterprise system of record, the natural one is the store itself. It holds business objects for many tenants and keeps each object's history by effective date. It answers "what was the value as of this date" and never lets one tenant see another's data. It's the entire design in miniature. The two hardest invariants, tenant isolation and correct effective-dated reads, both live in that one class. There a candidate either states a clean rule or hand-waves.
The problem below strips the domain down to what runs and keeps the two invariants. Every operation is keyed by tenant first, so a lookup in one tenant can never surface another's key. A read as of a date returns the latest version at or before that date. A correction to an existing effective date overwrites that version's value and leaves the rest of the history intact. The exercise comes down to getting the tenant scoping and the as-of boundary exactly right, much as a min-stack comes down to the duplicate case.
Tenant-scoped effective-dated store: isolation and as-of reads
Problem
key within its tenant, and its value changes over time. Every change carries an integer effectiveDate (think of it as a day number) from which that value is in effect. Reading an object means asking for the value in effect as of some date. Implement the store:-
RecordStore() creates an empty store.-
set(tenant, key, effectiveDate, value) records that key in tenant has value from effectiveDate on. If a version already exists at that exact effectiveDate, this is a correction: overwrite that version's value and leave every other version untouched. Returns the number of distinct versions stored for that (tenant, key) afterward.-
get(tenant, key, asOf) returns the value in effect as of asOf, meaning the value of the version with the greatest effectiveDate that is less than or equal to asOf. Returns null if the key does not exist in that tenant, or if asOf is before its first version.-
history(tenant, key) returns the full version history as a list of [effectiveDate, value] pairs sorted by effectiveDate ascending. Returns an empty list if the key does not exist in that tenant.-
keys(tenant) returns the keys held in that tenant, sorted ascending, and never any key from another tenant.The exercise centers on tenant isolation and the as-of boundary. An operation in one tenant must never see another tenant's data. A read at a date exactly equal to a version's
effectiveDate returns that version itself.Input encoding. A test case is a JSON array of operations. The first entry is
["RecordStore", []]; each later entry is [method, [args]]. The expected array holds each operation's return, with null for the constructor.Input: RecordStore(); set("acme","emp1",10,"grade-3"); set("acme","emp1",20,"grade-4"); get("acme","emp1",15); get("acme","emp1",5)
Output: set returns 1 then 2; get(...,15) returns "grade-3"; get(...,5) returns null
Two versions of emp1 exist: grade-3 from day 10 and grade-4 from day 20. As of day 15 the version in effect is grade-3 (10 is the greatest effective date at or before 15). As of day 5 no version has taken effect yet, so the answer is null.
Input: RecordStore(); set("acme","emp1",1,"a"); set("globex","emp1",1,"b"); get("acme","emp1",1); keys("acme")
Output: get returns "a"; keys("acme") returns ["emp1"]
Two tenants each have a key named emp1. The read in acme returns acme's value, never globex's, and acme's key list contains only its own key.
Nest maps: tenant -> key -> (effectiveDate -> value). The set method is a three-branch map write, and a correction reuses the same effectiveDate slot. Get collects the effective dates at or before asOf and takes the value at the maximum. History sorts the inner map, and keys sorts the middle map. Tenant isolation needs no extra code, because you never look outside the requested tenant's sub-map.
Tenant-scoped effective-dated store: isolation and as-of reads
Click "Run" to execute your code
Test Cases
[["RecordStore",[]],["set",["acme","emp1",10,"grade-3"]],["set",["acme","emp1",20,"grade-4"]],["get",["acme","emp1",15]],["get",["acme","emp1",25]],["get",["acme","emp1",5]],["set",["acme","emp1",20,"grade-5"]],["get",["acme","emp1",25]]]Expected: [null,1,2,"grade-3","grade-4",null,2,"grade-5"][["RecordStore",[]],["set",["acme","emp1",1,"a"]],["set",["globex","emp1",1,"b"]],["get",["acme","emp1",1]],["get",["globex","emp1",1]],["keys",["acme"]],["get",["globex","emp2",1]]]Expected: [null,1,1,"a","b",["emp1"],null][["RecordStore",[]],["set",["acme","emp1",10,"g3"]],["set",["acme","emp1",5,"g2"]],["history",["acme","emp1"]],["get",["acme","emp1",7]]]Expected: [null,1,2,[[5,"g2"],[10,"g3"]],"g2"][["RecordStore",[]],["set",["acme","zeta",1,"z"]],["set",["acme","alpha",1,"a"]],["keys",["acme"]],["keys",["ghost"]],["get",["acme","missing",5]]]Expected: [null,1,1,["alpha","zeta"],[],null][["RecordStore",[]],["set",["acme","emp1",10,"g3"]],["set",["acme","emp1",20,"g4"]],["get",["acme","emp1",10]],["get",["acme","emp1",20]],["get",["acme","emp1",9]]]Expected: [null,1,2,"g3","g4",null]What the round rewards
The bar on this design is a clean object model, honest isolation, and correct history. Scale math matters little. Whichever enterprise domain you get, the moves are the same. Frame the problem as a system of record before you draw anything, so you model the business instead of sizing a feed. Make the isolation decision first and name its cost, because it colors the schema and the blast radius of every bug. Put identity and role-based authorization in front of everything. Make the tenant match the outer gate, so a real role in one tenant can never reach another. Model the domain objects as configurable and effective-dated, so the shape bends per tenant and the past is never erased. Keep the write store strongly consistent. Push heavy reporting and slow fan-out onto their own store and idempotent workers. And design for the noisy neighbor and the cross-tenant leak as well as the clean demo. Do that and you read as someone who could own a piece of a real enterprise system. Then layer Workday's configurability, effective-dating, audit, and integrations on top. The same design carries the company flavor without a rewrite.
Key Takeaways
- ✓Build the general multi-tenant SaaS first, then layer the company flavor. The decisions come in a fixed order because each constrains the next: tenant isolation, identity and role-based authorization, a configurable business-object service, storage and consistency, then background jobs and reporting. A candidate who only knows the Workday answer but cannot design this from scratch fails.
- ✓Isolation is the invariant the whole system defends. Choose the model on purpose: a shared schema with a tenant key for many small tenants, a dedicated database for the few that demand it. Enforce it structurally, in the request context and at the database with row-level security. Then a forgotten filter can't leak one company's data into another.
- ✓Model the domain as configurable and effective-dated, which is what turns a CRUD store into a system of record. Object shape is per-tenant configuration instead of fixed columns. Each record is a timeline of versions keyed by effective date. A read is a floor lookup as of a date, and a correction never erases the past.
- ✓Keep the write store strongly consistent, because it holds pay and legal state. Push reporting to a separate store that is allowed to lag. Run slow fan-out on idempotent workers behind a queue, so a crash-and-retry never double-pays. Defend the bad day: meter work per tenant against the noisy neighbor, and prove no tenant can read another's rows.
- ✓The runnable core is the store itself, a tenant-scoped effective-dated map where isolation is structural and the as-of read is an inclusive floor lookup. Workday's distinctives, configurability, effective-dating, audit, and integrations, are each one of these decisions turned up, so the same design speaks the enterprise language without a rewrite.
Workday interview guideKnow what Workday asks before you walk in.
10 coding · 3 system design · 15 behavioral questions
Get their questions first.
InterviewLegend is independent and not affiliated with or endorsed by any company it covers.