InterviewLegend

Design an Ontology

Signature problem
Alex Chen45 min readUpdated 36 real interview reports
In this lesson
  • Scope the Ontology: object types, typed links and actions over integrated data
  • Grow the design from object types and actions, before any box diagram
  • Go deep on atomic actions, reversible entity resolution, object security and live schema changes

Understanding the problem

The prompt

Design an ontology for an intelligence agency or a bank. Model the real-world things it cares about, such as people, organizations, assets, and events. Model the typed relationships between them and the operations an analyst can perform on the model. Sit it on top of data already integrated from many source systems, and let operators both read the graph and take actions that change it.

The ontology is the concept Palantir owns, and no other company in this course has an equivalent. So be precise about what the word means before drawing anything. An ontology is a semantic layer that sits over data already pulled together from many systems and connects each row to the real-world thing it describes. A vessel, a wire transfer, a person of interest, a piece of equipment. Palantir often calls a mature one a digital twin of the organization, because the model mirrors the actual things the organization operates on rather than the tables those things happen to live in. The ask sounds like "build a knowledge graph," and half of it is exactly that. The other half is the part most candidates miss, and it is where the round is won.

That other half is action. A knowledge graph tells you what exists and how it connects. An ontology also models what you can do to it. An analyst does not just view that a shipment is delayed. They flag it, reassign it, open a case, escalate it. Every one of those is a defined operation with rules about who may run it and what it changes. Palantir splits the model into two halves along exactly this line. The semantic half is objects, their properties, and the typed links between them. The kinetic half is the actions layered on top. Name that split early and the interviewer knows you understand what the platform sells. Skip it and you have designed a graph database. That is a fraction of the answer.

The Palantir angle
The kinetic layer, actions on top of the graph, is the single highest-signal move in this design, because it is the part a generic answer never has. Palantir's Ontology is described as a multimodal system woven from four threads: data, logic, action, and security, all running through the same model. A plain graph store gives you the first thread. The strong answer models object types with typed properties, typed link types between them, and action types that change state under validation. Then it treats security as something the object carries, never a check bolted on at the edge. Ground it in the real product. Foundry uses the ontology as the operational layer over integrated commercial data, and Gotham uses it over the entity graph for defense and intelligence users. If your model would look identical at a consumer social app, you have modeled the graph and left out the half that makes it Palantir.

Functional requirements

State a tight scope and let the interviewer escalate from there. Escalation is how this round works. The core is a small set of capabilities. Define the schema: object types with typed properties, typed link types between them, and action types. Read the model: look up an object, traverse its links, and get a live view of changes. Change the model through actions, never through raw edits, so every mutation runs through a validated operation. And back the whole thing on the canonical datasets that integration already produced, resolving records so one real-world thing maps to one object.

In scope

  • Define object types (typed properties, a primary key, a backing dataset) and typed link types between them
  • Define action types: parameterized, validated operations that change objects and links, the kinetic layer
  • Read the model: get an object, traverse a link one hop, and subscribe to a live view of changes
  • Back objects on canonical datasets and resolve duplicate records so one real-world entity is one object

Below the line (name it, then defer)

  • The integration pipeline that produces the canonical datasets (its own design; this sits on top of its output)
  • The full access-control engine (named here as a property of the object; the marking and clearance machinery is its own design)
  • Full-text and geospatial search across the graph (its own design; here we support id lookup and one-hop traversal)
  • The lineage and audit backbone (referenced as per-value provenance; the immutable-log design is separate)

Non-functional requirements

These targets decide the round, so reason through each with something concrete rather than an adjective. Correctness comes first. An action either fully applies or does not. So the graph is never caught showing half of a change, such as a link created to an object that was never written. Governance is next, and it is mandatory. The permissions that gate an object travel with the object and are checked when it is read, so a hidden object cannot surface through a link traversal or a count. Reads should feel interactive. An object lookup plus a one-hop traversal wants to land in the low tens of milliseconds so an analyst UI stays fluid. The schema has to evolve while the system runs, because customers redefine what they model as the work changes. And every property value should trace back to the source row it came from, so "where did this number come from" always has an answer.

One tension runs through the whole design. The model wants to look like clean, stable real-world objects, but it is fed by messy, duplicated, drifting source data that changes underneath it. Those pull against each other. Two source systems describe the same person differently, a feed arrives late and revises a value, an analyst merges two objects and later has to pull them apart again. The design resolves this by keeping the ontology a resolved, governed view layered over versioned canonical data, never a second copy that drifts from its sources. The line that does not move is correctness under governance. A slightly stale read is survivable. A leak is not. An object that reaches someone without clearance, or an action that half-applies and corrupts the graph, is the failure this system exists to prevent. Assume, too, that it may run fully on-premise or air-gapped, so avoid leaning on any single cloud provider's managed service as if it will always be there.

  • Correctness: Actions apply atomically. An action commits all of its object and link writes or none of them, so a traversal never lands on a half-created link or a missing object.
  • Governance: Security rides the object. Markings and clearances gate each object and are evaluated at read time, so a hidden object never leaks through a link, a facet, or a count.
  • Read latency: Low tens of ms. Object lookup plus a one-hop traversal has to feel interactive in an analyst UI, which pushes the object backend toward indexed, in-memory-fronted reads.
  • Live schema: Evolves with no downtime. Object types, link types, and action types change while the platform runs, since customers redefine the model as the mission changes.

The set-up

Core entities

Name the handful of concepts the model is built from before drawing anything, and keep the schema-versus-instance distinction crisp, because the interviewer will test whether you hold it. Four carry the design.

1

Object type

The schema for a real-world concept: a named type such as Vessel, Person, or Wire Transfer, with a set of typed properties, a primary key, and one or more backing datasets. It is the template, not a thing in the world. Defining a new object type is a modeling act, not a code deploy, which is what lets non-engineers shape the model.

2

Object

One instance: a specific vessel, with concrete property values, a stable identifier that survives across source refreshes, and the markings that gate who may see it. Each object is backed by one or more canonical rows, and entity resolution is what guarantees one real-world thing maps to exactly one object rather than five duplicates.

3

Link type

A typed, directed relationship between two object types, such as Person owns Asset or Vessel located-at Port, with its own properties and a cardinality. Links are first-class, not foreign keys buried in a table, because traversal (find everything connected to this object) is the primary way analysts move through the model.

4

Action type

The kinetic layer: a parameterized, validated operation that changes the model, such as "reassign shipment" or "open case." It declares its inputs, the validation rules that must hold, and the exact object and link writes it performs. Analysts and automations change the graph only through actions, never through raw edits, so every mutation is defined, checkable, and auditable.

Make one split explicit before going further, because it is the spine of the design. The semantic side is the Schema store holding object types, link types, and action types, plus the Object backend holding the actual objects and links that reads traverse. The kinetic side is the Write engine that runs actions, and the Change log that records every mutation so subscriptions and lineage can follow along. Underneath both sits the Canonical datasets that integration already produced, which the objects are backed by rather than copied from. Say the words semantic and kinetic out loud when you draw this. Objects and links are what exists, actions are what you can do, and both live in one model. That framing is what Palantir means by an ontology, and naming it is what separates this answer from a graph database with extra steps.

API

Keep the interface thin and let the guarantees live in the notes, because the guarantees are what the interviewer probes. Reads look like graph navigation: fetch an object, walk a link, watch for changes. Writes funnel through a single door. There is no raw mutate call at all. Every call is evaluated against the caller's clearances before it returns a single object.

The interface
Request
operatorontologyfetch one object by type and id
getObject(type, id)
{ object } or not_found
returns property values the caller is cleared to see; a marking the caller lacks makes the object read as not_found, never a partial leak
operatorontologywalk a typed link one hop from an object
traverse(objectId, linkType, direction)
{ objects[] }
returns only linked objects the caller is cleared for; hidden neighbors are filtered out of the result and its count, not just the payload
operatorontologyrun a state-changing operation on the model
applyAction(actionType, params)
{ committed, changedIds[] } or rejected
validates params against the action type, then commits every object and link write as one atomic transaction; a validation or permission failure changes nothing
operatorontologyget a live view of objects matching a query
subscribe(query)
stream of change events
the change log drives the stream, so an operator UI updates as actions commit; each event is re-checked against the subscriber's clearances before delivery

Two details carry the contract. There is no raw write endpoint. Everything that changes the model goes through applyAction, which validates its parameters against the action type and checks the caller may run it. Then it commits every object and link write it performs as one atomic unit. A rejected action leaves the graph exactly as it was. And every read call is governed, not just filtered after the fact. When traverse returns three neighbors instead of five, the two the caller cannot see are gone from the result and from its count. An analyst can never infer a hidden object's existence from a number that does not add up. Those two habits, actions as the only write path and governance evaluated on every read, are what make the model both trustworthy and safe.

High-level design

Walk the model the way a request moves through it, and build the simplest thing that works before optimizing. A read resolves the schema, checks clearances, and pulls objects and links. A write goes through the action path, validates, commits atomically, and emits a change event that live views and lineage both consume. In the diagram, color marks each role. Blue is the Operator. Violet covers the services that run logic: the Ontology service, the Write engine, and the Policy engine. Amber is the Change log. Green is the durable data: the Schema store, the Object backend, and the Canonical datasets the objects are backed by.

Loading diagram...

The pieces connect like this. On a read, the Operator calls the Ontology service, which resolves the requested types against the Schema store. It asks the Policy engine which objects and values the caller may see. Then it reads the surviving objects and links from the Object backend. On a write, the service hands the action to the Write engine, which validates it against its type. It commits the object and link writes to the Object backend as one atomic transaction and emits an entry to the Change log. Live subscriptions ride that log back out through the service. Underneath, the Canonical datasets feed the object backend, so a source refresh revises the objects it backs without the ontology holding a second, drifting copy.

1) A read resolves the schema, then the clearances, then the data. The Operator asks for an object or a one-hop traversal. The Ontology service first looks up the object and link types in the Schema store so it knows the shape of what is being asked for. Then it calls the Policy engine to intersect the markings on the candidate objects with the caller's clearances, dropping anything they cannot see before the payload is ever assembled. Only the survivors are read from the Object backend. It is indexed by id and by link, so a traversal is a lookup instead of a scan. It is fronted by memory, so the round trip stays in the low tens of milliseconds. The subtle part is that the count comes from the filtered set, so a hidden neighbor never shows up as a suspicious gap, and that governed-traversal problem is the security deep dive.

2) A change goes through an action, never a raw edit. The Operator calls applyAction. The Write engine validates the parameters against the action type in the Schema store, such as "the target shipment exists and is not already closed." It also confirms the caller may run this action at all. Then it commits every object and link write the action performs as a single atomic transaction against the Object backend. A reader can never catch the graph showing a new link to an object that was not written. On commit it emits one entry to the Change log. If validation or the permission check fails, the whole action aborts and the model is untouched. Keeping actions atomic across several objects at once is the write deep dive.

3) Change events fan out to live views and to lineage. Every committed action lands in the Change log as an ordered, durable entry. Two very different consumers read it. Live subscriptions relay events back through the Ontology service, so an analyst watching a case sees it update within a second of the action committing. Each event is re-checked against that subscriber's clearances before delivery, so the log cannot leak. The audit and lineage systems read the same stream to answer who changed what, when, and why. Because the log is ordered and replayable, a subscriber that disconnects catches up from its last position instead of losing events, and a downstream materialized view can be rebuilt by replaying from the start. Evolving the schema without breaking those consumers is the last deep dive.

Sizing the ontology

Size the model here, rather than a request firehose. Palantir's scale is measured in things reconciled and rules enforced rather than reads per second. Take a large deployment. A rich domain is modeled with hundreds to low thousands of object types. So the schema itself is small, on the order of thousands of definitions. It fits in memory and versions cheaply. The instances are where the size lives. Assume roughly one billion objects across all types, each with around ten typed properties. Links run several times the object count, since a real domain is densely connected, so call it five billion links. Those two numbers, a billion objects and a few billion links, are the anchors the rest follows from.

Now the storage and the load. Say each object averages a couple of kilobytes, once you count ten property values, a provenance pointer per value, and its markings. A billion objects is then on the order of two terabytes, and five billion links at roughly a hundred bytes each add another half a terabyte. Provenance roughly doubles the property footprint. That is the price of tracing every value to its source. The read load is interactive rather than web-scale. Think thousands of object and traversal calls per second across all analysts, each needing to return in low tens of milliseconds, which is what pushes the object backend to be indexed and memory-fronted. The write load is far lower but far stricter. Actions might run at tens to hundreds per second, and every one must be atomic, validated, governed, and logged. So the write path is optimized for correctness, not throughput.

  • Schema: Hundreds to low thousands of types. Object, link, and action types together are only thousands of definitions, so the schema store stays small, in-memory, and cheap to version as it evolves.
  • Instances: ~1B objects, ~5B links. A densely connected domain runs several links per object; these two numbers anchor storage and index sizing, roughly 2 TB of objects plus 0.5 TB of links.
  • Read load: Thousands/sec, low tens of ms. Interactive analyst traffic, id lookup plus one-hop traversal, which is why the object backend is indexed by id and by link and fronted by memory.
  • Write load: Tens to hundreds of actions/sec. Low volume but every action is atomic, validated, governed, and logged; the write path is tuned for correctness and audit, not raw throughput.

Technology choices

Every box so far has been a category. In the room, name a real kind of technology for each and be ready to defend the category over the brand. The interviewer's favorite follow-up is what happens when this piece fails or the deployment is air-gapped. Headline the recognizable choice, and keep the exotic internals as trivia.

  • The Schema store is a small, versioned metadata store, the shape a config or catalog service takes, backed by a transactional database such as Postgres. It holds object, link and action type definitions, which are few in number but change often and must stay consistent. A relational store with real transactions and version history fits better than a big distributed store built for volume. Because it is small it lives in memory in front of that database, so type resolution on the read path costs nothing. The failure probe is a bad schema change. Types are versioned, so a broken definition rolls back to the prior version. Readers pinned to the old version keep working while it is fixed.
  • The Object backend is a graph-shaped store with an inverted-index-style link layer, the role a graph database like Neo4j or JanusGraph plays, fronted by an in-memory cache for hot objects. The category fits because the dominant read is "give me this object and everything linked to it," which a graph or adjacency index answers in a lookup while a row store would join. It is indexed by object id and by link type so traversal never scans. The failure probe is a cache or replica loss. Objects are a governed, rebuildable view over the canonical datasets and the change log. A lost cache is refilled and a lost replica is re-derived; neither is a source of truth on its own.
  • The Write engine runs actions as atomic transactions, the guarantee a transactional database or a lightweight transaction coordinator provides. It fits because an action touches several objects and links at once and must be all-or-nothing, so a validation failure or a crash mid-commit leaves the model untouched. Optimistic concurrency, a version check on each object it writes, handles two analysts acting on the same object at once by making the second retry against fresh state. The failure probe is a crash between committing the write and emitting the change event. Write the event in the same transaction (the outbox pattern), and the log can never miss a committed action.
  • The Change log is an ordered, durable, append-only log, the role Kafka plays, or an equivalent self-hosted queue where a managed one is not available. A log fits because two consumers, live subscriptions and lineage, both need every change in order. Both need to replay from a position after falling behind. A log gives that for free, and a fire-and-forget notification does not. Partitioning by object type or id keeps ordering where it matters while allowing parallelism. The failure probe is a slow or crashed subscriber: because the log retains events and tracks each consumer's offset, a subscriber resumes from where it stopped rather than dropping updates.
  • The Policy engine is an attribute-based access-control evaluator, the category Open Policy Agent represents, sitting inline on every read. It fits because the decision is a per-request intersection of the markings on the data, the caller's clearances and their declared purpose. It is evaluated fresh each time, so a revoked clearance takes effect immediately. It is co-located with the ontology service to keep the check off the network. The failure probe is fail-open versus fail-closed: on a policy-engine error the only safe behavior for classified data is to deny, so the system fails closed and returns nothing rather than risk a leak.

The brand names are the easy part. What you are defending is the split the whole design rests on. There are two planes. One is the semantic model, the schema store and the object backend, where objects and typed links are read fast and governed on every path. The other is the kinetic and provenance plane: the write engine and the change log. Actions commit atomically and every change is recorded in order. Live views, audit and lineage all follow from the same stream. Be explicit about which store makes a read fast and which component makes a write atomic. Say why a change has exactly one durable home, and why security is evaluated on the object instead of at the door. Miss that split and the answer reads like a graph database with boxes bolted around it.

Potential deep dives

Most of the interview signal lives in the deep dives. This design has more of them than usual, because the ontology touches modeling, integration, transactions, governance, and evolution all at once. An interviewer picks one or two and pushes until something gives, and at Palantir they lean toward the modeling and governance dives over raw scaling. Here are the five that come up most, in the order a design grows. First what the model is and where it gets its data. Then how it changes, how it stays safe, and how it evolves.

1) The model: objects, links, and the action layer

Start with what "ontology" buys over the two things people reach for instead. The round turns on the gap between them. Most first answers land on one of two shapes: raw tables, or a knowledge graph. Both are real. Both are incomplete. Walking from one to the next is how you show you understand the layer Palantir sells. Keep one anchor in mind. An analyst does not want to query the data. They want to operate on the world the data describes, and the model has to close that gap.

BadExpose the integrated tables directly

Approach

Leave the canonical datasets as tables and let analysts and apps query them with SQL. It is the least work, the data is already there, and any question is expressible as a join.

Challenges

It hands every consumer the same burden: reassemble the real-world thing from scattered rows, every single time. A person lives across a name table, an address table, and three source-specific tables, and every app has to re-join them and re-apply the same business rules to make sense of it. Nothing is shared, so two teams model "person" two different ways and quietly disagree. And there is no place to put actions or governance, so permissions get reinvented per query as WHERE clauses that leak. Bring this up only to explain why a shared semantic layer has to exist above the tables.

GoodA knowledge graph: object types, properties, typed links

Approach

Define object types with typed properties over the tables, and typed link types between them, so a person is one object with owns and works-for links to other objects. Now every consumer traverses the same shared model instead of re-joining rows, one real-world thing is one node, and "everything connected to this object" is a traversal rather than a pile of joins. This is a large step and it is genuinely most of the value.

Challenges

It models what exists but not what you can do. An analyst still cannot reassign a shipment, open a case, or flag an entity through the model, so the write path falls back to raw edits that skip validation and leave no defined trail. Governance is still bolted on around reads rather than carried by the objects. The graph is correct and shared, but it is inert. It describes the world without letting anyone change it under rules, which is exactly the half a real deployment needs most.

What forces the upgrade is that the platform's users are operators, not just analysts. They act on the world. Every action has rules about who may run it and what it may change. Modeling those actions as first-class, validated operations is the difference between a graph you look at and a model you run an operation on. That kinetic layer is the piece Palantir names explicitly and the piece most candidates leave out.

GreatAdd action types: the kinetic layer over the graph

Approach

Layer action types on top of the objects and links. Each action declares its parameters, the validation rules that must hold before it runs, and the exact writes it performs, and it is the only way to change the model. "Reassign shipment" checks the shipment is open and the operator owns the route, then rewrites the assigned-to link and stamps a status, all atomically. Now the model has all four threads Palantir describes: data (objects and properties), logic (validation), action (the operations), and security (permissions carried on the object and checked per action). Analysts and automations both go through the same actions, so every change is defined, validated, governed, and recorded.

Challenges

The cost is that actions are real logic that has to live somewhere and stay consistent with the schema. The write engine validates against the current type definitions and commits across several objects atomically, which is the next two deep dives. The payoff is decisive. State the semantic-versus-kinetic split plainly, objects and links are what exists and actions are what you can do, and give one concrete action end to end. That single distinction is the clearest signal in the whole round that you know what an ontology is rather than reaching for a graph database.

The Palantir angle
Naming the kinetic layer, and giving one action end to end, separates a strong Palantir answer from a generic one. Palantir describes the Ontology as woven from data, logic, action, and security together, and the action thread is the one no competitor's model has. Candidates who stop at objects and typed links have described a knowledge graph, which the interviewer has seen a hundred times. The ones who add action types, with validation and atomic writes, and who say that security is a property the object carries rather than a filter at the edge, are describing the actual product. Do not just assert it. Walk one action: the parameters, the rule it checks, the links it rewrites, the event it emits. That concrete pass is worth more than any amount of box-drawing.

2) Backing objects on canonical data and resolving entities

An object is only as trustworthy as the data under it. So the next dive is where objects come from and how "one real-world thing is one object" is enforced. The objects are not a second copy of the data. They are a resolved, governed view over the canonical datasets that integration produced. The hard problem is that many source rows, from many systems, describe the same real-world entity in slightly different ways. Two banks spell a counterparty differently, a watchlist and a travel record refer to the same person with no shared id. If each source row became its own object, the graph would show five people where there is one, and every traversal would be wrong. Entity resolution is what collapses those rows into a single object.

The resolution itself runs as a pipeline that a strong answer sketches rather than hand-waves. Do not hand-wave it. Candidate rows are blocked into rough groups so you are not comparing everything to everything. They are then scored with fuzzy matching on names, identifiers, and context. The high-confidence matches merge automatically, while the ambiguous ones go to a human to confirm or reject. That human-in-the-loop step matters at Palantir, because a wrong merge in an intelligence or financial context has real consequences. The system keeps a person in the decision instead of trusting the model blindly. Each resolved object records which source rows it was built from and carries per-value provenance, so you can always answer which system a given property came from.

One detail earns seniority here. Merges have to be reversible, and interviewers reach for exactly this as an escalation. A resolved object keeps the independent histories of its constituents rather than flattening them into one blob. So when new evidence shows two records were wrongly merged, the object splits back apart and each property is re-attributed to the constituent it came from. Design for un-merge from the start. Store the object as a set of linked source contributions with their provenance, never an overwritten single record. Then "pull these two apart" is a re-partition of contributions instead of an impossible un-scramble. Flag reversibility from the start, because "now un-merge those two entities" is a near-certain follow-up and a flattened design has no answer for it.

The Palantir angle
Reversible entity resolution with a human in the loop is authentically Palantir, and the un-merge escalation is one of the most reliable follow-ups in this round. Foundry's entity resolution runs the full arc from fuzzy matching through model scoring to human validation. Resolved objects preserve their constituents' histories, so a merge can be undone and properties re-partitioned to where they came from. Design for it up front by keeping each object as a set of provenance-tagged source contributions rather than a single overwritten row. When the interviewer says the two people you just merged are actually different, the strong answer already has the mechanism: split them, re-attribute each property to its constituent, and let downstream links follow. A design that overwrote on merge simply cannot recover, and that is the gap the escalation is fishing for.

3) Applying an action atomically

An action usually touches more than one thing at once. That is what makes the write path interesting. "Open a case" might create a case object, link it to three entities, and flip a status on each, so five writes have to land together or not at all. If a reader could catch the graph with the case created but the links missing, a traversal would return a corrupt half-state. The promise that the model reflects the real world would break. The core requirement is atomicity across several objects, plus a way to handle two operators acting on the same object at the same moment.

BadWrite each object change independently

Approach

As the action runs, write each object and link change as it is computed, one call at a time, and return when the last one lands.

Challenges

A crash or a rejected write partway through leaves the model in a state no valid action ever produces. The case exists but two of its links are missing. Or a status flipped on one entity but not its pair. Readers see the torn state, and there is no clean way to roll back the writes that already committed. For a model whose entire value is faithfully mirroring the world, a partial action is corruption. Raise it only to motivate why the writes have to commit as one unit.

GoodCommit the whole action in one transaction

Approach

Compute all of the action's writes first, validate them against the action type, then commit them together in a single transaction against the object backend. Either every object and link write lands or none does, so a reader never sees a half-applied action. A validation failure or a crash mid-commit aborts cleanly and leaves the model as it was. This is the atomicity the design needs, and for many actions it is enough.

Challenges

It is correct in isolation but silent about concurrency. Two analysts reassign the same shipment at the same time. Each reads the current state and computes a valid write. The second clobbers the first with no sign anything was lost. The transaction was atomic, but the two actions were not aware of each other. Under a model many people operate on live, that lost-update problem is common enough that it needs an explicit answer.

What forces the upgrade is concurrent operators on shared objects, which is the normal case, not an edge case. The action needs to notice that the object changed under it between the read and the commit, and refuse to silently overwrite. Optimistic concurrency gives exactly that with almost no cost on the common path where there is no conflict.

GreatAtomic commit with optimistic version checks and an outbox

Approach

Stamp every object with a version. When an action commits, it checks that each object it writes still holds the version it read, and the transaction only lands if every check passes. If another action moved an object in between, the commit fails and this action retries against fresh state, re-running its validation. The second reassignment sees the first and either proceeds correctly or is rejected. On the same transaction, write the change event into an outbox table alongside the object writes, and a relay publishes it to the change log. Because the event commits atomically with the data, the log can never miss a committed action or announce one that rolled back.

Challenges

The cost is retries under contention on a hot object, which you bound by keeping actions small and their validation cheap, so a retry is fast. When a single object is genuinely a hotspot, such as a shared counter, you serialize just that object rather than locking the whole action. The outbox adds a relay step and a brief lag between commit and the event appearing on the log, which is fine for a subscription that tolerates a second. State the ordering as a rule: validate, commit data and event together with version checks, then relay. That is how the write stays atomic, concurrent-safe, and consistent with the log all at once.

4) Security as a property of the object

Here governance is a property every object carries, and that matters the moment you traverse a link. The requirement is strict. An object the caller is not cleared to see must be invisible on every path. That includes appearing as a neighbor in a traversal or in any count or aggregate, so its existence cannot be inferred. The naive instinct is to filter the final result set. That leaks in a dozen quiet ways. So the dive is about pushing the check down onto the object itself and evaluating it everywhere reads happen.

The mechanism is markings that ride the data and are evaluated at read time. Each object, and where needed each property, carries markings. A marking is mandatory. If an object holds several, the caller must possess all of them to see it. Markings propagate through the model rather than being set once by hand. Objects are backed by canonical data with lineage, so an object inherits the markings of the source rows behind it. A value classified at its source stays classified everywhere it flows, including into any object derived from it. On top of markings sit classification-based controls for government-grade releasability rules and purpose-based controls that grant access for a declared, auditable reason. The policy engine intersects all of this with the caller's clearances on every request, which is why a revoked clearance takes effect on the very next read rather than whenever a cache expires.

The sharpest probe, the one to have an answer ready for, is leakage through aggregates and traversal. If a traversal returns "5 linked accounts" but the caller may only see three of them, the number two is a leak, because it tells them two hidden accounts exist. So the count has to be computed from the governed set, after the policy filter, not before. The same holds for facets and any derived total: they are calculated over what the caller can see, so a hidden object contributes to nothing they can observe. And because the deployment may be air-gapped, the whole check runs locally with no external policy call. It also fails closed, denying access if the policy engine cannot render a decision, since for classified data the only safe default is to show nothing.

The Palantir angle
Palantir's governance is mandatory and propagating, which is the opposite of the optional role checks most designs bolt on, and saying so is high-signal. Markings are mandatory controls: hold several and a user needs all of them, and they follow the data through lineage so a derived object inherits its sources' markings. Classification-based controls add releasability, and purpose-based controls grant access for a declared, auditable reason. The interviewer's favorite trap is the leaky aggregate, so answer it before they ask. Counts, facets and traversal results are all computed after the policy filter. A hidden object never surfaces as a suspicious gap. Add that the check runs at read time and fails closed, and that it holds in an air-gapped deployment with no outside call. That is government-grade access control described in the platform's own terms, and it is the correctness axis Palantir grades hardest.

5) Evolving the schema without downtime

The last dive is the one that reflects how Palantir is used: the model is never finished. Customers add object types and properties, split one type into two, and redefine links as the mission changes. None of that can take the platform down or invalidate the billions of objects already in it. This is what Palantir means by a dynamic ontology, a schema that is data rather than compiled code, so a modeler changes it live without a deploy. The requirement is that the schema evolve continuously while reads and actions keep flowing against whatever version of the model each of them expects.

Two mechanisms carry it. First, the schema is versioned, and changes are additive by default. Adding a property or a link type does not touch existing objects. They simply lack the new field until an action or a backfill sets it, and readers written against the old shape keep working. A destructive change, such as removing a property or splitting a type, is staged. The new shape is added, data is migrated in the background, consumers move over, and only then is the old shape retired. There is never a moment where the model is broken. Second, reads and actions resolve against a schema version, so an in-flight action validates against the definitions it started with rather than a definition that changed underneath it. Because the schema is small and memory-resident, resolving a version costs nothing on the hot path.

The real-time side rides on the same change log from the high-level design, so close the loop on it. Take a materialized view, such as a live map of all delayed shipments or a running case dashboard. It is kept fresh by consuming the change log rather than re-querying the whole object backend. So it updates within a second of an action committing, and it can be rebuilt from scratch by replaying the log if it is ever lost or a new view is added. That is the same append-only stream the write engine emits to, doing double duty for live subscriptions, for lineage and audit, and for these derived views. Now picture the interviewer's escalation: a feed arrives seven days late with new columns. The schema-as-data answer holds. The drift is absorbed as an additive schema change and a backfill, the objects already in the model keep serving, and nothing has to come down.

Key Takeaways

  • ✓The ontology is a semantic model over integrated data, and its defining half is the kinetic layer. Model object types with typed properties, typed link types between them, and action types that change state under validation. Name the semantic-versus-kinetic split and walk one action end to end. That single distinction is what separates this from a graph database.
  • ✓Objects are a resolved, governed view over canonical data, not a second copy. Resolve records so one real-world thing is one object, and keep a human in the loop. Make merges reversible from the start by storing provenance-tagged contributions. "Now un-merge those two" is a near-certain escalation.
  • ✓Every change goes through an action, and actions commit atomically. Validate against the action type, and commit all object and link writes in one transaction with optimistic version checks. Write the change event in the same transaction, so the log never misses a committed action or announces a rolled-back one.
  • ✓Governance is a property the object carries, not a gate at the door. Markings are mandatory and propagate through lineage. The check runs at read time and fails closed. Counts and traversal results are computed after the policy filter, so a hidden object never leaks through an aggregate. Assume it must all hold air-gapped.
Had your Palantir interview?Tell us what they asked. Reviewed before it goes in, never with your name.
Palantir interview guide

Know what Palantir asks before you walk in.

20 coding · 6 system design · 17 behavioral questions

Get Premium$49 a year · Cancel anytime
Or start freeNo card.

Get their questions first.

InterviewLegend is independent and not affiliated with or endorsed by any company it covers.