Production Readiness and Mastery Review (LangGraph in Production, Module 16)
Production Readiness and Mastery Review (LangGraph in Production, Module 16)
This is Module 16 of LangGraph in Production, a free 16-module course that takes you from your first StateGraph to a deployed, durable autonomous coding agent on LangGraph v1.x. Start at Module 1 or browse the full syllabus.
The last Monday morning question
It is 8
a.m. A GitHub issue lands in Tally’s tracker:bug: negative balances not caught on import. By 8, Forge has planned the fix, edited two files in parallel, run the test suite, opened a pull request, and paused for your approval — fully persisted, streamed, and traced in LangSmith. That is what you shipped at the end of Module 15.
Now your tech lead asks the harder question: “Is Forge actually production-ready, or did you just build something that works in a lab?”
You know the sandbox is isolated. The checkpointer is in place. Auth is wired into LangGraph Platform. But can you explain, without looking at the code, why you picked durability="async" instead of "exit"? Why your Supervisor is hand-built instead of using langgraph-supervisor? What happens — exactly — when a user resumes an interrupt() and the node re-executes from the top?
This module adds no new code. It consolidates the 15 modules you have just completed, gives you a production checklist to audit Forge against, a self-diagnostic grid to find your weak spots, 30 mastery questions with explained answers, and a map of where to go from here. The difference between a developer who “got LangGraph working” and a practitioner who can defend every decision in a senior engineering interview lives in this module.
In this module
You will learn to:
- Audit a LangGraph v1.x deployment against a five-category production checklist and articulate when LangGraph is the wrong tool.
- Self-assess your mastery of the full theory inventory (CA1–CA14) and know exactly which module to re-read for each gap.
- Work through 30 mastery questions covering every concept area, from core mechanics to production scenarios to interview-style debates.
- Articulate the key design decisions in Forge —
create_agent,interrupt(),durability=, the Send API, the hand-built Supervisor — and defend them under pressure.- Navigate the broader LangGraph ecosystem: comparisons with CrewAI, AutoGen, PydanticAI, and smolagents, plus concrete next-step paths.
You will build: Nothing — and that is the point. This module is pure consolidation: a production checklist, a self-diagnostic grid, a 30-question mastery bank with explained answers, and a real deployment debrief from shipping Forge.
Concepts covered: CA1–CA14 — the full LangGraph v1.x theory inventory. This module reviews every concept area you have built across M1–M15.
Prerequisites: Modules 1–15 complete; Forge v1.0 deployed (the M15 lab executed, the M14 deployment in place). Recommended but not required: your notes from the Mastery corners in M1–M15.
Where you are in the course
M1 ✅ M2 ✅ M3 ✅ M4 ✅ M5 ✅
M6 ✅ M7 ✅ M8 ✅ M9 ✅ M10 ✅
M11 ✅ M12 ✅ M13 ✅ M14 ✅ M15 ✅
M16 👉 (you are here — the final module)
Fifteen modules ago you wrote a while-loop agent. Today you have a stateful, durable, human-checkpointed, multi-agent system deployed on LangGraph Platform and evaluated against a golden test set in LangSmith. This is the only module with no Forge increment — there is nothing left to build. The work now is understanding what you built and being able to defend every decision. That is the real capstone.
T5 — Production Mastery: Checklist, Self-Diagnostic, Question Bank, and What Comes Next
The Journey from M1 to M16 in One Diagram
Before diving into the checklist, orient yourself with the full arc of what you built:
flowchart LR M1["M1\nStateGraph\nfoundations"] --> M2["M2\nForgeState\n+ reducers"] M2 --> M3["M3\nControl flow\n+ test-fix cycle"] M3 --> M4["M4\nTools\n+ create_agent\n+ Planner"] M4 --> M5["M5\nSend API\nmap-reduce"] M5 --> M6["M6\nPersistence\nthreads"] M6 --> M7["M7\nHITL\ninterrupt()"] M7 --> M8["M8\nTime-travel\ndebug"] M8 --> M9["M9\nDurability\nresilience"] M9 --> M10["M10\nthe Store\nmemory"] M10 --> M11["M11\nStreaming\nprogress"] M11 --> M12["M12\nSubgraphs\nmulti-agent"] M12 --> M13["M13\nFunctional API\n@entrypoint"] M13 --> M14["M14\nDeploy\nPlatform+Studio"] M14 --> M15["M15\nEval\nForge v1.0"] M15 --> M16["M16\nReadiness\nreview"] style M16 fill:#2a9d8f,color:#fff
Each arrow is a module. Each node is a concept area that you built into a running system. M16 has no arrow coming out of it — because Forge is done.
H2.1 — The LangGraph Production Checklist
Finishing the labs does not mean Forge is production-ready. A lab that mocks network calls and runs against a local SQLite database will pass pytest even when RetryPolicy is absent, even when the sandbox leaks to the real repo, even when auth is missing from a public deployment. The checklist below maps each category to the module that built it. Work through it honestly — a “no” is a gap to close before you call this production-grade.
⚠️ Common misconception: “If I’ve completed all 15 modules, Forge is automatically production-ready.”
False. Completing the labs proves you understand the concepts. It does not prove your deployment configuration is correct. The most common gaps:
durability=left at the implicit default when the use case actually needs"exit"or"sync";RetryPolicymissing on any node that calls the LLM or runs a subprocess; the sandbox leaking to the realrepo_pathwhentempdiris not initialized correctly; custom auth absent on a Platform deployment exposed to the internet. Work the checklist category by category.
Category 1 — Durability and Resilience (M9)
-
durability=is explicit on every criticalinvoke/streamcall. You have chosen deliberately among"exit"(persist only at graph completion — maximum throughput, checkpoint loss on mid-run crash),"async"(persist after each node, async write — balanced default), and"sync"(persist before executing the next node, sync write — maximum durability, latency cost). -
RetryPolicyis attached to every node that touches a network resource or an LLM:RetryPolicy(initial_interval=0.5, backoff_factor=2.0, max_attempts=3, jitter=True). Two lines. No excuse not to have them. -
CachePolicyis attached to the repo-analysis node (Planner’sread_file/list_files/search_codephase) to avoid re-reading the same repo on retries. - Resume-after-crash is tested: kill the process mid-run, restart, re-invoke the same
thread_idwithinput=None. Forge should resume from the last checkpoint without re-executing completed nodes.
Category 2 — Agentic Security (M3, hardened M9)
- Forge always operates on a sandbox copy of Tally (
forge/sandbox.py,tempdir). The originalrepo_pathis never modified directly. -
pytestruns in an isolated subprocess (subprocess.run,cwd=sandbox_path,timeout=60, network disabled or not relied upon). No shell injection via the issue text. - Secrets in
.env(ANTHROPIC_API_KEY,LANGSMITH_API_KEY) — never hardcoded. A grep gate in CI catches any literal key pattern. - Custom auth is wired (
forge/auth.py,@auth.authenticate,langgraph.json) if Forge is deployed on LangGraph Platform and exposed beyond localhost. A deployment without auth on a public endpoint is not production-ready, full stop. - RCE/SSRF mitigations are reviewed: no user-controlled input reaches
subprocess.runwithout sanitization; no outbound HTTP calls from the sandbox to arbitrary URLs. Review the 2026 LangGraph RCE/SSRF disclosures if you have not.
Category 3 — Persistence and State Management (M6)
- The checkpointer matches the environment:
InMemorySaverfor unit tests only,SqliteSaverfor local or single-process production,PostgresSaverfor scaled or multi-process deployments. -
.setup()is called onSqliteSaverorPostgresSaverbefore the first run. Forgetting this throws a table-not-found error on the first checkpoint write — the kind of bug that shows up only in production, not in tests that mock the checkpointer. -
thread_idis a UUID generated per session or per user request. Never reuse athread_idacross unrelated runs. -
ForgeStatefield hygiene:plan_approved,pull_request, andrevieware not present in the TypedDict before the modules that introduced them (M7 and M12). No field exists in the schema before its concept area is built. This matters forupdate_statecalls — writing to a field the graph does not know about yet causes silent data loss.
Category 4 — Observability and Evaluation (M15)
-
LANGSMITH_TRACING=true,LANGSMITH_API_KEY, andLANGSMITH_PROJECTare set in the deployment environment. Without tracing, debugging a production failure means reading logs instead of inspecting full LLM call traces. - The golden set of 10 Tally issues (
evals/golden_set.json) is in CI. The LangSmith pytest plugin gate blocks a merge if evaluation scores drop below threshold. - Cost is measured per run for both
fastandsmarttiers. Context is pruned ifmessagesgrows beyond a manageable token budget across multiple Fixer/Tester cycles. - The judge model in your eval suite is different from the candidate model. Using the same model to judge its own outputs inflates scores systematically (self-preference bias).
Category 5 — Platform Deployment (M14)
-
langgraph.jsonpoints toforge/graph.pyand the compiled graph is exported as a variable (or via a factory function). The file has been validated bylanggraph devbefore being used forlanggraph up. - You validated
langgraph dev+ LangGraph Studio before runninglanggraph upwith Docker. Studio is your first debugging surface for deployment issues — use it before going to the full Docker stack. - Double-texting strategy (
multitask_strategy=) is configured deliberately for your use case:"reject"(ignore concurrent runs on the same thread),"enqueue"(queue them),"interrupt"(cancel the running run and start fresh), or"rollback"(cancel and revert to last checkpoint). -
langgraph-cli(v0.4.x as of June 2026) andlanggraph-sdk(v0.4.x as of June 2026) are still in 0.x — re-check the changelog before every upgrade. Breaking changes in these packages do not follow semver conventions yet.
Note on naming: Since October 2025, the LangGraph Platform managed hosting has also appeared under the name “LangSmith Deployment” in some dashboards and documentation. Both names refer to the same service. If you see “LangSmith Deployment” in a menu where you expect “LangGraph Platform”, you are not lost — they renamed it. The underlying SDK and
langgraph.jsonformat did not change.
When NOT to Use LangGraph
This was first raised in Module 1 (CA1.5) and deserves a full treatment now that you have seen the entire framework.
Single LLM call. If your use case is one prompt and one response, init_chat_model(...).invoke(messages) is the right tool. A StateGraph with one node is pure orchestration overhead.
Linear pipeline without branching or cycles. If your steps always run in the same order with no conditional routing, no loops, and no parallelism, Python function composition or LangChain Expression Language (LCEL) costs you nothing extra and is easier to read and test.
Stateless transform. Batch embeddings, OCR, image resize, format conversion — none of these need persistent state, agent loops, or graph semantics. LangGraph is the wrong abstraction.
Team without solid Python experience. LangGraph v1.x with StateGraph, Pydantic v2, uv, and proper type hints requires Python 3.10+ literacy. Underestimating this creates debt that compounds.
Ultra-low latency requirements (under 200 ms) with a near-zero budget. The Pregel superstep engine, the checkpoint writes, and the persistence layer add overhead. For a stateless, high-throughput API endpoint, that overhead is not justified.
The golden rule: “If you are not going to use persistence, human-in-the-loop, cycles, or parallelism, you are paying orchestration tax for no benefit.”
Real Deployment Debrief (TODO — Decision L10)
“I built and deployed Forge on LangGraph v1.x before writing this module (decision L10). Here is what I encountered in production that the labs did not fully prepare me for:”
TODO:AUTHOR— Lesson 1 (e.g.,durability="async"vs"exit"under real load: the async write buffer behavior under concurrent runs, and why you may want"sync"for the plan-approval node specifically).TODO:AUTHOR— Lesson 2 (e.g., the interrupt re-execution trap in production: the node runs from the top on resume, so a write-file side effect beforeinterrupt()fired twice before I added idempotence guards).TODO:AUTHOR— Lesson 3 (e.g., the real cost delta betweenfastandsmarttiers on a 10-file Tally issue: the smart tier’s Sonnet model costs roughly 8x more per run, which matters at scale).TODO:AUTHOR— Lesson 4 (e.g., streaming subgraphs withsubgraphs=True: the namespace prefixing makes events readable, but you need to strip it client-side if you are building a UI on top).TODO:AUTHOR— Lesson 5 (e.g., the LangGraph Platform / LangSmith Deployment renaming in October 2025 caused confusion in the team dashboard — both labels appear depending on which page you are on).(These placeholders are replaced by the real debrief before Module 16 is published — decision L10. If you are reading this before the author has deployed Forge, the placeholders will still be here.)
H2.2 — Your Self-Diagnostic Grid: CA1–CA14 → Modules to Revisit
Read each row. Answer the test question honestly, without looking anything up. If you are unsure, mark “revisit” and go back to the primary module before interviews or before going to production.
| Concept area | Self-assessment test question | Primary module | Also read |
|---|---|---|---|
| CA1 — Foundations | Without re-reading the course, explain the Pregel/superstep model in two sentences. Can you name three concrete reasons NOT to use LangGraph? | M1 | M16 §H2.1 |
| CA2 — State & reducers | Why is edits the only operator.add field in ForgeState? What does last-write-wins imply for the route field? | M2 | M3, M5 |
| CA3 — Control flow | When do you prefer Command(update=..., goto=...) over a conditional edge? What is the difference between recursion_limit and the attempts guard? | M3 | M7, M12 |
| CA4 — Tools & agent loop | Which import replaces create_react_agent in v1.x? Why is ToolNode(handle_tool_errors=True) always set? | M4 | M12, M13 |
| CA5 — Parallelism | Which argument to add_node forces the fan-in to wait for all Editors? From which module is Send imported in v1.x? | M5 | M12 |
| CA6 — Persistence | Which method must be called before using SqliteSaver? What does get_state(config) return? | M6 | M7, M8, M9 |
| CA7 — HITL | Why does the node re-execute from the beginning after an interrupt() resumes? What is the shape of the v1.x Interrupt object? | M7 | M8, M14 |
| CA8 — Time-travel | Which API creates an alternative branch without erasing history? What is the difference between replay and fork? | M8 | M14 Studio |
| CA9 — Durability | What are the three durability= modes and their trade-offs? What is the difference in role between RetryPolicy and CachePolicy? | M9 | M13, M15 |
| CA10 — Long-term memory | What is the fundamental difference between the Store and the checkpointer? How do you pass the Store to a node? | M10 | M12, M15 |
| CA11 — Streaming | Which stream mode is required for custom events? Which function emits events from inside a node? | M11 | M12, M14 |
| CA12 — Multi-agent | Why are langgraph-supervisor and langgraph-swarm broken-forward with create_agent v1? How do you hand off to a parent graph using Command? | M12 | M14 |
| CA13 — Functional API | When do you choose @entrypoint/@task over StateGraph? What does previous store between runs? | M13 | M9 idempotence |
| CA14 — Deploy & eval | What does “LangSmith Deployment” mean and how does it relate to “LangGraph Platform”? What is multitask_strategy=? Why must the judge differ from the candidate? | M14, M15 | M16 §H2.1 |
Scoring guidance: If you mark “revisit” on four or more rows, go back to the primary modules before interviews. If you mark “revisit” on CA7 or CA9, those are the most common production failure modes — prioritize them.
H2.3 — The 30-Question Mastery Bank
Format: 30 questions, 4 options each (A/B/C/D). Answer all 30 before checking the answer key below. Recommended: print or copy the question block, annotate your answers, then scroll to the answers.
Distribution: CA1–CA2 (Q1–Q4), CA3 (Q5–Q6), CA4 (Q7–Q8), CA5 (Q9–Q10), CA6 (Q11–Q12), CA7 (Q13–Q14), CA8 (Q15–Q16), CA9 (Q17–Q18), CA10 (Q19–Q20), CA11 (Q21–Q22), CA12 (Q23–Q24), CA13 (Q25–Q26), CA14 (Q27–Q28), transversal (Q29–Q30).
Q1 [CA1 — Foundations] You are explaining LangGraph’s execution model to a colleague. Which statement most accurately describes the Pregel/superstep model?
A. Each node runs as soon as its inputs are ready, in a fully asynchronous event loop. B. All nodes in the same “super step” run concurrently; the graph does not advance to the next step until all nodes in the current step have finished. C. Nodes run strictly sequentially in the order they were added to the graph. D. The Pregel model is a LangGraph-specific abstraction with no relation to the original Google Pregel system.
Q2 [CA1 — Foundations] A team wants to build a pipeline that (1) calls an LLM to summarize a document, (2) formats the summary as JSON, and (3) sends it to a webhook. There is no state to persist, no branching, and no user interaction. What is the most appropriate tool?
A. StateGraph with three nodes and an InMemorySaver checkpointer.
B. LangChain Expression Language (LCEL) chaining or simple Python function composition.
C. The Functional API (@entrypoint/@task) with durability="exit".
D. A StateGraph is always preferred over plain Python because it provides better error handling.
Q3 [CA2 — State & reducers] In ForgeState, the edits field is declared as list[FileEdit] with an operator.add reducer. The messages field uses add_messages. Which statement is correct?
A. Both fields use the same merge strategy: the incoming value always replaces the stored value.
B. operator.add on edits appends the new list to the existing list; add_messages on messages deduplicates by message ID and handles deletions.
C. operator.add on edits deduplicates by file path to prevent editing the same file twice.
D. Without an explicit reducer, both fields would default to operator.add because lists require merging.
Q4 [CA2 — State & reducers] A node in ForgeState sets route = "bug". Another node in the same superstep sets route = "feature". What happens?
A. LangGraph raises a ReducerConflictError because two nodes wrote the same field in the same superstep.
B. The last write wins — the final value of route is whichever node finished last, because route has no reducer and uses last-write-wins semantics.
C. Both values are stored and route becomes ["bug", "feature"].
D. The graph halts and requires manual resolution via update_state.
Q5 [CA3 — Control flow] Your team is debating durability= settings. A colleague says “we should use checkpoint_during=False to skip mid-run checkpoints and improve throughput.” What do you tell them?
A. That is correct — checkpoint_during=False is the recommended way to skip mid-run checkpoints in v1.x.
B. checkpoint_during= is a legacy argument from LangGraph 0.x. In v1.x, the equivalent is durability="exit", which persists only at graph completion.
C. checkpoint_during=False is not a valid argument — the correct argument is save_checkpoints=False.
D. There is no way to control when checkpoints are written in LangGraph v1.x; all nodes always checkpoint.
Q6 [CA3 — Control flow] You need a node that always saves its output before the next node starts, even at the cost of extra latency. Which durability= value is correct?
A. "exit" — saves at graph completion only.
B. "async" — saves after each node, but asynchronously (the next node may start before the write finishes).
C. "sync" — saves synchronously before the next node starts, guaranteeing the checkpoint is committed first.
D. "immediate" — saves mid-node after each tool call.
Q7 [CA4 — Tools & agent loop] A developer shows you this import in a v1.x codebase: from langgraph.prebuilt import create_react_agent. What is your assessment?
A. This is the correct v1.x import — create_react_agent is the recommended agent factory.
B. create_react_agent was deprecated in LangGraph 1.0 and removed in 2.0. The v1.x equivalent is from langchain.agents import create_agent. Using this import in a v1.x codebase is a freshness trap.
C. The import is valid, but you should use create_agent from langchain.agents only when you need structured output.
D. create_react_agent and create_agent are identical; the difference is purely cosmetic naming.
Q8 [CA4 — Tools & agent loop] In Forge, the Planner uses create_agent with response_format=Plan. A junior engineer asks why you use ToolNode(handle_tool_errors=True) instead of catching exceptions in each tool function. What is the correct explanation?
A. handle_tool_errors=True is required for create_agent to work — without it, tool calls fail silently.
B. ToolNode(handle_tool_errors=True) catches tool exceptions and returns them to the LLM as ToolMessage content, allowing the agent to reason about the failure and retry or change strategy — instead of crashing the whole graph.
C. handle_tool_errors=True retries the tool call automatically up to three times before returning an error.
D. ToolNode is only compatible with create_agent; if you write a ReAct loop manually you must handle errors in each function.
Q9 [CA5 — Parallelism] Which import is correct in LangGraph v1.x for using the Send API in a fan-out?
A. from langgraph.constants import Send
B. from langgraph.graph import Send
C. from langgraph.types import Send
D. from langchain_core.messages import Send
Q10 [CA5 — Parallelism] In Module 5, the fan-in node for edits is added with defer=True. What does defer=True do?
A. It delays the node’s execution by one superstep to allow slow network calls to complete.
B. It tells LangGraph to wait until all parallel branches sending to this node have finished before executing it, enabling a proper fan-in from a dynamic number of parallel senders.
C. It prevents the node from writing to the checkpoint until the graph completes.
D. It marks the node as optional — if no Send targets it, the node is skipped silently.
Q11 [CA6 — Persistence] You initialize a SqliteSaver but forget to call .setup(). What happens on the first graph run?
A. LangGraph silently creates the required tables automatically.
B. The graph runs without checkpointing — data is lost on restart but no error is raised.
C. An error is raised when LangGraph tries to write the first checkpoint because the required tables do not exist.
D. .setup() is optional; it only matters if you want to use get_state_history.
Q12 [CA6 — Persistence] get_state(config) returns a StateSnapshot. Which fields does StateSnapshot contain that are most useful for debugging a paused run?
A. Only the current state values — no metadata.
B. values (current state), next (the next nodes to execute), config (with checkpoint_id), metadata, created_at, and tasks. Together these tell you exactly where the graph is paused and what it will do next.
C. values and history — a full log of all previous states.
D. StateSnapshot is only useful after the graph has completed; it is empty for paused runs.
Q13 [CA7 — HITL] Forge pauses at the plan-approval interrupt(). The user approves and calls Command(resume="approved"). Which statement accurately describes what happens next?
A. The graph resumes from the line immediately after interrupt() in the node function.
B. The node that called interrupt() re-executes from the very beginning. Any code before the interrupt() call runs again. This means side effects (like writing files) before the interrupt must be idempotent or guarded.
C. The graph creates a new thread and runs the remaining nodes in that thread.
D. The interrupt() return value replaces the entire node’s output, so the node body does not run again.
Q14 [CA7 — HITL] You want to pause Forge before the Planner runs so you can inspect what it receives, without modifying any Forge source code. Which mechanism do you use?
A. interrupt() — add it to the Planner node source.
B. A static interrupt breakpoint configured at compile time: graph.compile(interrupt_before=["planner"], checkpointer=checkpointer). The graph pauses before executing the Planner node without any code change inside the node.
C. Command(goto="__interrupt__") from the previous node.
D. Static breakpoints are not supported in v1.x — you must add interrupt() calls to the node source.
Q15 [CA8 — Time-travel] A Forge run crashed after the Editor node with a bad plan. You want to fix the plan and re-run from the Editor step, without losing the checkpoint history. Which approach is correct?
A. Delete the thread and start a fresh run with the corrected plan as input.
B. Call graph.update_state(config, {"plan": corrected_plan}, as_node="planner") to inject the corrected plan at the Planner checkpoint, then invoke the graph from that checkpoint. LangGraph creates a new branch in the history — the original run is preserved.
C. Call graph.replay(checkpoint_id=..., input={"plan": corrected_plan}) to overwrite the checkpoint.
D. You cannot modify state mid-history; you must replay the full run from the beginning with corrected input.
Q16 [CA8 — Time-travel] What is the difference between replaying a run from a checkpoint and forking a run from a checkpoint?
A. Replay re-executes all nodes from the specified checkpoint forward; fork only replays the failed node.
B. Replay re-executes the graph from a checkpoint using the saved inputs, without modifying state. Fork (via update_state) modifies the state at a checkpoint and then continues from that modified state — creating a new branch in the history.
C. Replay and fork are synonyms in LangGraph — both terms refer to the same update_state operation.
D. Fork requires a new thread_id; replay uses the same thread_id.
Q17 [CA9 — Durability] The Tester node in Forge calls subprocess.run(["pytest", ...]). This can fail transiently due to file-system race conditions. You add a RetryPolicy. What is the role of RetryPolicy versus CachePolicy on the same node?
A. RetryPolicy retries the node on failure; CachePolicy caches the node’s output so it is not re-executed if inputs have not changed. They address different problems: transient failures vs. redundant re-computation.
B. RetryPolicy and CachePolicy cannot be applied to the same node — they are mutually exclusive.
C. CachePolicy is a superset of RetryPolicy — if you cache the output, retries become unnecessary.
D. RetryPolicy applies only to LLM calls; for subprocess failures you must catch exceptions manually.
Q18 [CA9 — Durability] You deploy Forge on a server that restarts every night. A run is in progress when the server shuts down. With durability="async" and a PostgresSaver, what happens when the server comes back up?
A. The run is lost — you must restart it from the beginning with the original input.
B. The run can be resumed by invoking the graph with the same thread_id and input=None. LangGraph reads the last committed checkpoint from Postgres and continues from that node.
C. The run is automatically restarted by LangGraph Platform without user intervention.
D. With durability="async", the latest checkpoint may not have been committed before the crash, so resuming could re-execute the last completed node. Using durability="sync" guarantees the checkpoint is committed before the next node starts.
Q19 [CA10 — Long-term memory] What is the fundamental difference between the Store and the checkpointer in LangGraph?
A. The Store is faster than the checkpointer because it does not serialize state to disk.
B. The checkpointer persists state per thread_id — it is scoped to a single conversation or run. The Store is cross-thread: it uses (namespace, key) and persists data across runs and users. Use the checkpointer for run state; use the Store for long-term, shared knowledge.
C. The Store replaces the checkpointer in v1.x — you no longer need both.
D. The checkpointer stores the full graph state; the Store stores only tool outputs.
Q20 [CA10 — Long-term memory] In Forge’s Planner node, how do you access the Store to retrieve past Tally conventions?
A. Pass the Store as a parameter to build_graph() and access it via a global variable inside the node.
B. Inject the Store via the node signature using Runtime.store or the get_store() helper from langgraph.runtime, which LangGraph injects automatically at call time.
C. Access the Store from the state dict: state["store"].get(namespace, key).
D. The Store is only accessible from @entrypoint/@task nodes, not from StateGraph nodes.
Q21 [CA11 — Streaming] You want to emit a custom event from inside the Tester node showing which test file is currently running, so the CLI can display live progress. Which mechanism do you use?
A. stream_mode="updates" — the node’s state update automatically becomes a real-time event.
B. get_stream_writer() from langgraph.types, called inside the Tester node. This returns a StreamWriter that you can call with any JSON-serializable payload. The client receives it under the "custom" stream mode.
C. astream_events(version="v2") with event_type="on_tool_start" — this fires automatically when a subprocess starts.
D. Custom events require the Functional API (@task) — they are not available in StateGraph nodes.
Q22 [CA11 — Streaming] A client wants to receive LLM token-by-token output from Forge’s Planner, plus structured state updates whenever the Tester finishes. Which stream mode combination is correct?
A. stream_mode="messages" only — it includes both token-level and state-level events.
B. stream_mode=["messages", "updates"] — "messages" gives token-by-token LLM output; "updates" gives node-level state diffs after each node completes.
C. stream_mode="values" — it includes all events in a single stream.
D. Combining modes is not supported; you must run two separate stream calls.
Q23 [CA12 — Multi-agent] A developer proposes using langgraph-supervisor (the official package) with create_agent to build Forge’s Supervisor. What is your response?
A. That is the recommended approach — langgraph-supervisor is the canonical way to build supervisors in v1.x.
B. langgraph-supervisor and langgraph-swarm are broken-forward with create_agent v1 (issue langgraphjs#1739). The correct approach is to build the Supervisor by hand: a node that routes via Command(goto=...) based on the current state, with handoffs implemented manually.
C. langgraph-supervisor works with create_agent — you just need to pass compat_mode="v1".
D. langgraph-supervisor is fine for prototyping but should be replaced before production.
Q24 [CA12 — Multi-agent] In Forge’s multi-agent architecture, the Reviewer subgraph needs to hand off control back to the parent Supervisor graph after generating its review. Which Command argument achieves this?
A. Command(goto="supervisor", update={"review": review_result})
B. Command(graph=PARENT, update={"review": review_result}) — graph=PARENT tells LangGraph to exit the current subgraph and return control to the parent graph’s next node.
C. Command(resume="parent", update={"review": review_result})
D. Return the review result as a plain dict — LangGraph automatically exits the subgraph when a node returns without a Command.
Q25 [CA13 — Functional API] You want to rewrite Forge’s test-fix loop using the Functional API. Which combination of decorators is correct, and what does previous store?
A. @graph on the outer function and @step on each inner function. previous stores the last run’s full graph state.
B. @entrypoint on the outer function and @task on each inner async function. previous stores the return value of the @entrypoint function from the previous invocation on the same thread_id.
C. @entrypoint on the outer function only — @task is optional and only needed for parallel execution.
D. @workflow on the outer function and @task on inner functions. previous is always None on the first run.
Q26 [CA13 — Functional API] When should you choose @entrypoint/@task over StateGraph for a new agent?
A. Always prefer the Functional API — it is newer and will replace StateGraph in a future version.
B. Prefer StateGraph when you need fine-grained control over conditional edges, parallel branches, or explicit state schema. Prefer the Functional API when your logic is naturally expressed as Python functions with await, you want to avoid defining a typed state schema, or you are migrating existing async Python code.
C. Use the Functional API only when your agent has more than 10 nodes — for smaller graphs, StateGraph is required.
D. The Functional API does not support HITL (interrupt()) — if you need human approval, use StateGraph.
Q27 [CA14 — Deployment] You are deploying Forge on LangGraph Platform (also known as LangSmith Deployment since October 2025). Your langgraph.json defines the graph. A colleague asks what langgraph dev does versus langgraph up. What do you say?
A. They are aliases — both commands start the same production server.
B. langgraph dev starts a local development server with hot-reload and connects to LangGraph Studio for visual debugging. langgraph up builds and starts the full production Docker stack. Always validate with langgraph dev + Studio before running langgraph up.
C. langgraph dev deploys to the cloud; langgraph up runs locally.
D. langgraph dev is deprecated in favor of langgraph serve.
Q28 [CA14 — Evaluation] In your LangSmith evaluation suite for Forge, you use Claude Sonnet as both the judge model and the candidate model (the model being evaluated). A peer flags this as a problem. What is the issue?
A. There is no issue — using the same model as judge and candidate is efficient and produces unbiased results. B. A model used to judge its own outputs exhibits self-preference bias: it systematically rates its own outputs higher than an independent judge would. Use a different model family or provider as the judge to get reliable evaluation scores. C. The issue is purely regulatory — using one API provider for both roles violates LangSmith’s terms of service. D. Self-judging is only a problem when the model has been fine-tuned on the evaluation data.
Q29 [Transversal — CA6 + CA7 + CA9] Forge is running on a remote server. After the Planner generates a plan, the HITL approval interrupt() fires. The server crashes while waiting for user input. The user comes back two hours later and wants to approve the plan. Which sequence of steps recovers the run correctly?
A. The run is permanently lost — interrupt() state is not persisted through a server crash.
B. Start the graph from scratch with the same issue input. LangGraph deduplicates automatically.
C. (1) Verify that the SqliteSaver or PostgresSaver is on durable storage. (2) Re-invoke the graph with the same thread_id and input=None — LangGraph restores the last checkpoint, which includes the pending interrupt(). (3) Inspect the pending interrupt value with get_state(config). (4) Call Command(resume="approved") to continue the run from the plan-approval step.
D. Call get_state_history(config) to find the last checkpoint, then call update_state to set plan_approved=True and restart from the beginning.
Q30 [Transversal — CA9 + CA5 + CA3] Forge is processing a large Tally issue. The Planner generates targets for 8 files. The fan-out dispatches 8 parallel Editor tasks via the Send API. During the fan-in, two Editor nodes fail with transient errors. You have RetryPolicy(max_attempts=3) on each Editor and durability="async". Which statement correctly describes the recovery behavior?
A. If any Editor fails after all retries, the entire graph fails and must be restarted from the beginning.
B. Each Editor retries independently up to 3 times. If a specific Editor still fails, LangGraph marks its Send branch as failed. With durability="async", the checkpoints from the successful Editors have been persisted. On resume (same thread_id, input=None), LangGraph replays only the failed branches — the 6 successful Editors do not re-run.
C. RetryPolicy applies only to the full graph, not to individual nodes in a Send fan-out.
D. With durability="async", the checkpoint may not include all 8 Editor outputs even if they succeeded, so all 8 must re-run on resume to guarantee correctness.
Answer Key
Q1 — B. The Pregel model processes all “ready” nodes in a superstep concurrently. The graph advances to the next superstep only when all nodes in the current one have finished. This is what makes LangGraph’s parallelism predictable and its state consistent at each step. Option A describes a reactive event loop (not Pregel). Option C describes a sequential pipeline (not LangGraph). Option D is false — LangGraph explicitly credits and implements the Pregel model.
Q2 — B. A linear, stateless, three-step pipeline has no need for persistence, cycles, HITL, or parallelism. LCEL chaining or plain Python function composition is lighter, faster, and more readable. Adding a StateGraph introduces overhead with no benefit. Options A, C, and D all reach for LangGraph when it is not the right tool.
Q3 — B. operator.add for edits concatenates lists — new FileEdit items are appended. add_messages for messages is smarter: it merges by id, handles RemoveMessage, and avoids duplicates. Option A is wrong because operator.add is not last-write-wins. Option C is wrong — operator.add does not deduplicate by path. Option D is wrong — the default for a field without a reducer is last-write-wins, not operator.add.
Q4 — B. Fields without an explicit reducer use last-write-wins semantics. If two nodes in the same superstep write to route, the value from whichever write is processed last wins. Option A is wrong — there is no ReducerConflictError for last-write-wins fields. Option C would require a list reducer. Option D would require a special conflict resolution hook, which does not exist by default.
Q5 — B. checkpoint_during= is a legacy argument from LangGraph 0.x. It is not valid in v1.x. The correct v1.x mechanism is durability="exit", which tells the runtime to persist state only at graph completion. Citing checkpoint_during= in a v1.x codebase is one of the top-10 freshness traps.
Q6 — C. "sync" writes the checkpoint synchronously before starting the next node, guaranteeing the checkpoint is durable before execution continues. "exit" (option A) only persists at completion — it does not help for mid-run durability. "async" (option B) is the balanced default but the write may not complete before the next node starts. "immediate" (option D) does not exist.
Q7 — B. create_react_agent from langgraph.prebuilt was deprecated when LangGraph 1.0 shipped (October 22, 2025) and removed in 2.0. The v1.x replacement is from langchain.agents import create_agent. Option A is the most dangerous leurre in a 2026 interview. Options C and D are both wrong: create_agent is the standard v1.x agent factory regardless of structured output, and the two functions are not equivalent.
Q8 — B. ToolNode(handle_tool_errors=True) catches exceptions in tool calls and converts them into ToolMessage content with the error, returning it to the LLM so it can reason about the failure and adjust. Without this, any tool exception propagates as a Python exception and crashes the node. Option A is wrong — create_agent does not require this setting. Option C is wrong — retry is handled by RetryPolicy, not handle_tool_errors. Option D is wrong — ToolNode works with any agent architecture that uses tool calls.
Q9 — C. The correct v1.x import is from langgraph.types import Send. Option A (langgraph.constants) is the legacy 0.x import — using it in a v1.x codebase is a freshness bug. Options B and D are wrong locations entirely.
Q10 — B. defer=True is a fan-in synchronization mechanism. It tells LangGraph to collect all incoming Send values and wait for all parallel branches to complete before executing this node. Without it, the fan-in node might execute before all Editors have finished. Option A is a plausible-sounding but entirely invented behavior. Options C and D are also wrong.
Q11 — C. .setup() creates the schema tables in the SQLite (or Postgres) database. Without it, the first checkpoint write fails with a missing-table error. Option A is wrong — LangGraph does not auto-create tables. Option B is wrong — the absence of tables raises an error, it does not silently skip checkpointing. Option D is wrong — .setup() is required before any checkpointing, not just for get_state_history.
Q12 — B. A StateSnapshot contains values (current state as a dict), next (list of node names to execute next), config (includes checkpoint_id for time-travel), metadata, created_at, and tasks (pending writes). This is the full picture of a paused run. Options A, C, and D are all incomplete or incorrect descriptions.
Q13 — B. This is the most important HITL trap in production. When Command(resume=...) is called, LangGraph re-executes the entire node function from the beginning, not from the line after interrupt(). Any side effect before the interrupt() call — writing a file, making a network request, incrementing a counter — runs again. You must design the code before interrupt() to be idempotent or guarded. Option A is the intuitive but wrong answer. Options C and D are invented behaviors.
Q14 — B. Static breakpoints (interrupt_before=[...] at compile time) pause before the specified node runs, without any modification to the node’s source code. They are primarily useful for debugging and inspection. interrupt() inside a node (option A) is for production HITL flows where the pause point and the data to present to the user are determined by runtime logic. Static breakpoints can be switched on/off at compile time without touching application code.
Q15 — B. update_state with as_node="planner" injects a state update at the Planner’s position in the checkpoint history, creating a new branch. The graph can then be invoked from that new checkpoint, continuing with the corrected plan. The original run’s history is preserved — you can always go back to it. Option A loses history. Option C invents a replay method with an input argument. Option D is wrong — this is exactly what update_state is designed for.
Q16 — B. Replay re-executes the graph from a checkpoint with the original saved inputs — you are replaying what happened. Fork (via update_state) modifies the state at a checkpoint and runs forward from the modified state, creating a divergent branch. Fork is for “what if I had done X differently”; replay is for “show me exactly what happened.” Options A and C are wrong. Option D is also wrong — fork uses the same thread_id but adds a new branch under a new checkpoint_id.
Q17 — A. RetryPolicy handles transient failures: if the node raises an exception, it retries with backoff. CachePolicy handles redundant re-computation: if the node’s inputs have not changed (same hash), it returns the cached output without running the node. They solve different problems and can both be applied to the same node. Options B, C, and D are all incorrect.
Q18 — D. Both B and D are partially correct, making this a nuanced question. Option B is correct that resumption is possible with the same thread_id and input=None. But option D adds important nuance: with "async", the latest checkpoint may not have been fully committed to Postgres at the moment of the crash, so the last completed node might re-run. The safest option for a node where idempotence is hard to guarantee is "sync". The best answer capturing this nuance is D.
Q19 — B. The checkpointer is per-thread (thread_id): state from Thread A is not visible from Thread B. The Store uses (namespace, key) addressing and is accessible from any thread. Conventional use: checkpointer for run-scoped state (messages, current plan, test results); Store for cross-run, cross-user knowledge (Tally coding conventions, patterns from past fixes, user preferences). Options A, C, and D are all wrong.
Q20 — B. The Store is injected via langgraph.runtime — either through Runtime.store in the node signature or get_store() at call time. LangGraph handles the injection automatically. Option A (global variable) works but is not the canonical pattern and does not compose well with different Store backends. Option C is wrong — the Store is not in the state dict. Option D is wrong — Store injection works in both StateGraph nodes and Functional API functions.
Q21 — B. get_stream_writer() is called inside the node and returns a StreamWriter callable. When you call it with a payload, LangGraph emits that payload as a custom stream event. The client receives it when streaming with stream_mode="custom" (or when "custom" is included in a list of modes). Option A is wrong — "updates" sends state diffs, not arbitrary custom payloads. Option C is wrong — astream_events with "on_tool_start" does not capture arbitrary subprocess events. Option D is wrong — get_stream_writer works in StateGraph nodes.
Q22 — B. stream_mode=["messages", "updates"] combines token-level LLM output ("messages") with node-level state diffs ("updates"). Options A and C are wrong because no single mode provides both. Option D is wrong — LangGraph supports combining multiple stream modes in a single call.
Q23 — B. langgraph-supervisor and langgraph-swarm use internal APIs that are broken-forward with create_agent v1 (issue langgraphjs#1739). The recommended approach in v1.x is to build the Supervisor by hand: a node function that reads the current state and returns Command(goto=...) to route to the appropriate subgraph or node. This is what Forge’s M12 implementation does. Option A is dangerously wrong. Option C invents a compatibility argument that does not exist. Option D understates the problem.
Q24 — B. Command(graph=PARENT) is the v1.x mechanism for exiting a subgraph and returning control to the parent. Combined with update={"review": review_result}, it passes the review data up to the parent state before transitioning. Option A would route within the same graph level, not exit the subgraph. Option C invents a "parent" string value that does not exist. Option D is wrong — subgraphs do not automatically exit on a plain dict return.
Q25 — B. The correct decorators are @entrypoint (outer function) and @task (inner async functions). previous stores the return value of the @entrypoint function from the most recent prior invocation on the same thread_id, enabling stateful behavior across runs without a typed state schema. Option A uses fictional @graph and @step decorators. Option C is wrong — @task is needed for subtask decomposition. Option D uses another fictional @workflow decorator.
Q26 — B. The Functional API is best for logic that is naturally expressed as Python async functions, when you want to avoid defining a TypedDict schema, or when you are wrapping existing Python code. StateGraph is better when you need explicit conditional edges, multiple parallel branches with Send, or a schema that other tools (like the Studio graph visualizer) can introspect. Option A is wrong — the Functional API is not replacing StateGraph. Option C is wrong — node count is not the criterion. Option D is wrong — interrupt() works in both the Functional API and StateGraph.
Q27 — B. langgraph dev runs a local development server with file-watching, hot-reload, and integration with LangGraph Studio. Use it to iterate quickly and visually inspect graph executions. langgraph up builds the production Docker image and starts the full stack. Always use langgraph dev + Studio to validate before langgraph up. Options A, C, and D are all incorrect.
Q28 — B. Self-preference bias is well-documented: LLMs tend to prefer outputs generated by models in their own family or even themselves, inflating evaluation scores. An independent judge — a different model from a different provider — gives more reliable signal. Option A ignores a real and documented bias. Option C is a fictional concern. Option D is wrong — self-preference bias exists regardless of fine-tuning.
Q29 — C. The interrupt() state is persisted in the checkpointer at the moment the interrupt fires. With a durable backend (SqliteSaver on local disk, PostgresSaver for scale), a server crash does not lose the pending interrupt. The recovery sequence is: confirm the durable backend is in place, re-invoke with the same thread_id and input=None, inspect the pending interrupt with get_state, then call Command(resume="approved"). Option A is wrong — interrupt() is fully persisted. Options B and D are incorrect recovery sequences.
Q30 — B. Each Editor node’s RetryPolicy is independent. Retries happen at the node level, not the graph level. With durability="async", successful Editor checkpoints are persisted to storage shortly after completion. On resume with the same thread_id and input=None, LangGraph reads the checkpoint and replays only the nodes that did not complete successfully — the 6 successful Editors are not re-run. Option A would be true only if there were no checkpointing. Option C is wrong — RetryPolicy applies at the node level including nodes in a Send fan-out. Option D overstates the risk of "async" mode under normal operating conditions.
Self-Score Grid
| Score | What it means | Recommended next step |
|---|---|---|
| 25–30 / 30 | Solid mastery. You can deploy Forge in production and defend every decision in a senior engineering interview. | Go to production. Review M16 §H2.4 for where to deepen. |
| 20–24 / 30 | Good level. Isolated gaps in specific concept areas. | Re-read the primary modules for every area where you got at least one question wrong. |
| 15–19 / 30 | Moderate gaps. Focus on CA3, CA7, and CA9 first — they are the most critical in production — then CA5 and CA12. | Re-read M3, M7, M9, M5, M12 before interviews or production deployment. |
| 0–14 / 30 | Foundational gaps. The fundamentals (CA1–CA4) and HITL (CA7) are prerequisites for everything else. | Re-read M1–M7 before moving on. Do not skip the labs. |
H2.4 — Where to Go Next
You have built and shipped Forge. Here is the honest map of where to go depending on your goals.
1. Advanced LangGraph Platform
The M14 deployment gave you langgraph dev, Studio, and the basic SDK. The production Platform has much more:
- The full SDK surface:
client.assistants.*,client.threads.*,client.runs.*,client.crons.*,client.store.*. - Scaling beyond a single process:
PostgresSaver+ Redis for distributed checkpoint storage. - Production webhooks:
client.runs.create(webhook=...)for async notification pipelines. - Granular custom auth: token scopes, per-assistant authorization, API key rotation via
forge/auth.py. - Reference: https://langchain-ai.github.io/langgraph/cloud/ (as of June 2026).
Note: since October 2025, the managed hosting is also labeled “LangSmith Deployment” in some dashboards. Same service, different label depending on the page you are on.
2. The LangChain Ecosystem
LangGraph is the runtime. The LangChain ecosystem wraps it:
- LCEL (LangChain Expression Language): stateless pipeline composition. The right tool when you do not need persistence, HITL, or cycles.
langchain-mcp-adapters: mount any Model Context Protocol server as a set of LangChain tools and pass them to Forge’screate_agent. This lets you extend Forge with external MCP tools (GitHub, Jira, Linear) without writing tool adapters from scratch.- Vector stores:
pgvector,Chroma,Pinecone— combine with the Store’s semantic search index (index={"embed": embedder, "dims": 1536}) for richer long-term memory.
3. LangGraph vs. the Alternatives
This is the table you pull out in an architecture review when someone asks “why not CrewAI?”
| Framework | Paradigm | State | HITL | Durability | When to choose |
|---|---|---|---|---|---|
| LangGraph v1.x | Graph (Pregel) | TypedDict/Pydantic, channels with reducers | interrupt() native, four HITL patterns | durability= (exit/async/sync) | Cycles, HITL, parallelism, durable production workloads |
| CrewAI | Role-based crew | Shared dict per crew run | Limited — no native interrupt-resume | Weak (in progress as of mid-2026) | Rapid prototyping of team-of-agents demos; simplicity over control |
| AutoGen | Conversational multi-agent | Message-based conversation history | Breakpoints (limited) | Partial — depends on integration | Research, multi-agent chat experiments, Microsoft ecosystem |
| PydanticAI | Functional, type-safe | Pydantic v2 native, stateless or minimal | Limited | None — stateless by design | Agents where type safety is the top priority; simple request-response flows; speed |
| smolagents | CodeAgent (Python-first) | Minimal | None | None | Lightweight coding agents; HuggingFace Hub integration; budget-constrained deployments |
Opinion, stated directly: LangGraph is the only framework in this table where “production-ready” means something precise: durable checkpointing, native HITL, observable with LangSmith, and deployable on a managed platform with auth, webhooks, and cron. The others are excellent for prototyping. Ask hard questions before putting any of them in a customer-facing pipeline where correctness on resume, human approval, and cost observability matter.
4. Next Courses in the Series
This is Course 4 in the AI From Zero catalogue.
- Course 3 — smolagents (HuggingFace): Lightweight CodeAgent architecture, HuggingFace Hub model integration, minimal state. Target audience: teams already in the HuggingFace ecosystem or projects with tight budgets where a full LangGraph deployment is overhead.
- Course 5 — PydanticAI: Functional agents with Pydantic v2 native state, strict type safety, no persistence overhead. Target audience: developers who want the guarantees of a typed schema with zero runtime surprise, for request-response agents that do not need persistence or HITL.
- Both series follow the same format as this one: free, English, code-first, one real system built progressively. Start at Module 1 of each series or browse the full catalogue at the course index.
In Production: The LangGraph Practitioner Mindset
LangGraph v1.x has a steep learning curve — but the curve is front-loaded. Once you have internalized the state/channel/reducer mental model (CA2), the Pregel superstep (CA1.3), and the interrupt-resume contract (CA7.2), the rest of the framework clicks into place. The concepts do not fight each other; they compose.
The most common production failure mode is not a LangGraph bug. It is an idiom mismatch: using v0.x patterns in a v1.x codebase. The patterns are often subtle — create_react_agent still has documentation that looks current; checkpoint_during=False still appears in tutorials from early 2025; from langgraph.constants import Send compiles without error. None of these break at import time. They break at scale, under load, or at the worst possible moment in a production run.
The ten freshness traps to know cold, in both directions:
create_react_agent(0.x,langgraph.prebuilt) →create_agent(langchain.agents)config_schema/config["configurable"]→context_schema+Runtime(langgraph.runtime)NodeInterrupt(removed) →interrupt()+Command(resume=)checkpoint_during=(legacy) →durability=(v1.x)from langgraph.constants import Send(legacy) →from langgraph.types import Send.contentstring literal on messages →.content_blocks(structured content)langgraph-supervisor/langgraph-swarmwithcreate_agent→ build the supervisor by handinterrupt_before=[...]as the only HITL mechanism → useinterrupt()for runtime-determined pausesMessageGraph(removed) →StateGraphwithMessagesStateget_graph().draw_mermaid()for Studio visualization →langgraph dev+ Studio renders this automatically
The second most common production failure: deploying without a RetryPolicy on any node that touches the network. RetryPolicy is two lines. Add it.
Mastery Corner
What to Really Understand Here
After 16 modules, three ideas are worth internalizing at a level beyond “I know what it does.”
First: the state/channel/reducer model is the theory of everything in LangGraph. Every other concept — parallelism, HITL, time-travel, streaming, the Store — builds on how state flows through channels. If you are ever confused by a LangGraph behavior, ask “what is the reducer doing here, and is it what I intend?” That question resolves 70% of production mysteries.
Second: interrupt() is a first-class persistence event, not a pause. When interrupt() fires, the entire graph state — including the pending interrupt value — is committed to the checkpointer. The graph is not “paused in memory”; it is persisted and can be resumed days later, from a different process, on a different machine. This is what makes HITL in LangGraph production-worthy. The corollary: the node re-executes from the beginning on resume, so idempotence before the interrupt() call is not optional.
Third: the portfolio beats the credential. LangGraph has no certification. What you have instead is Forge: a deployed, durable, HITL-capable, multi-agent coding system with LangSmith tracing and a CI evaluation gate, running on LangGraph v1.x. That is a more specific and verifiable claim than a badge score.
5 Mastery Corner Questions
(These five questions are distinct from the 30 in the question bank above. Answer all five before reading the answers.)
MC1 [CA12 — Interview/architecture] A developer says: “I’m using langgraph-supervisor because it’s the official LangGraph way to build a supervisor.” What is your response?
A. Agree — langgraph-supervisor is the canonical approach for supervisor architectures in v1.x.
B. langgraph-supervisor and langgraph-swarm use internal APIs that are broken-forward with create_agent v1 (issue langgraphjs#1739). The correct approach is to build the supervisor by hand: a routing node that reads state and returns Command(goto=...). This is what Forge’s M12 implementation does, and it is more transparent and maintainable than the package anyway.
C. langgraph-supervisor is fine but should be wrapped in a compatibility shim before production use.
D. langgraph-supervisor is correct for simple supervisors; for complex ones like Forge’s, you need langgraph-swarm.
MC2 [CA6 + CA8 — Production debugging scenario] Forge is deployed on LangGraph Platform. A run crashes after three nodes. Walk through how you (a) inspect the run history, (b) replay from the last valid checkpoint, and (c) fork to test a fix without overwriting the history.
A. (a) Check the application logs. (b) Re-run with the original input. (c) Deploy a fixed version and re-run.
B. (a) Use client.threads.get_history(thread_id) or graph.get_state_history(config) to list all checkpoints. (b) Invoke the graph with the specific checkpoint_id from the last valid checkpoint to replay from that point. (c) Call graph.update_state(config_at_checkpoint, state_patch, as_node="...") to inject a fix, creating a new branch — the original history is untouched and you can always go back to it.
C. (a) Use LangSmith trace viewer. (b) Call graph.retry(thread_id=..., from_node="..."). (c) Forks are not supported — create a new thread.
D. (a) Call graph.get_state(config) to see the current state only. (b) Re-run with input=None — LangGraph automatically replays from the crash point. (c) Create a new thread with update_state as the first call.
MC3 [CA7 — Interview/HITL pattern] A developer asks: “What’s the difference between interrupt_before=['planner'] at compile time and calling interrupt() inside the Planner node? Which one do I use in production?”
A. They are equivalent — use whichever is easier to configure.
B. interrupt_before=['planner'] is a static breakpoint: it always pauses before the Planner, regardless of runtime conditions. Use it for debugging and inspection — it requires no code change in the Planner. interrupt() inside the Planner is dynamic: you decide at runtime, based on the current state, whether to pause and what to present to the user. Use interrupt() for production HITL flows where the pause logic depends on the data (e.g., pause only if the plan has more than 5 files, or present the plan content for review).
C. interrupt_before is deprecated in v1.x — use interrupt() for all HITL cases.
D. interrupt() inside the node is for debugging; static breakpoints are for production.
MC4 [CA4 + CA9 — Freshness trap, interview] A candidate’s code review shows: from langgraph.prebuilt import create_react_agent and config["configurable"]["user_id"] for passing user context. How many freshness traps do you see, and what are they?
A. No traps — both are valid in v1.x.
B. One trap: create_react_agent is deprecated. config["configurable"] is still valid.
C. Two traps: (1) create_react_agent from langgraph.prebuilt was deprecated in LangGraph 1.0 and removed in 2.0 — the v1.x equivalent is from langchain.agents import create_agent. (2) config["configurable"] for passing application context was deprecated in v0.6 — the v1.x equivalent is context_schema + Runtime[Context] from langgraph.runtime.
D. Three traps: create_react_agent, config["configurable"], and the use of langgraph.prebuilt for any import.
MC5 [CA1 — Scenario/when not to use] A team wants to orchestrate a five-step ML pipeline: load data, clean, embed, index, and write to a vector store. Steps always run in order, there is no branching, no human approval, no cycle, and the state does not need to survive a crash. What is the honest answer about LangGraph?
A. Use LangGraph — it provides better observability for production ML pipelines.
B. LangGraph is not the right tool here. A linear, stateless, non-interactive pipeline with five sequential steps has no need for persistence, HITL, cycles, or parallelism. Using a StateGraph adds orchestration overhead with no benefit. Python function composition or a lightweight pipeline library is simpler, faster, and easier to reason about.
C. Use LangGraph with durability="exit" — this minimizes the overhead.
D. Use the Functional API (@entrypoint) — it has lower overhead than StateGraph and is the right choice for sequential pipelines.
Mastery Corner Answers
MC1 — B. langgraph-supervisor breaks forward compatibility with create_agent v1 due to internal API differences (issue langgraphjs#1739). Building the supervisor by hand — a simple routing node using Command(goto=...) — is the correct and supported approach. It is also more readable: you can see exactly what the routing logic does. Options A and D are both wrong and would lead to a broken production agent.
MC2 — B. The correct debugging sequence uses the LangGraph time-travel API. get_state_history (or its SDK equivalent) lists all checkpoints with their checkpoint_id. You replay by invoking the graph from a specific checkpoint using that ID. You fork by calling update_state at a checkpoint with a state patch — this creates a new branch in the history without modifying the original. The original branch is always accessible. Options A and C invent non-existent methods. Option D misunderstands get_state (it returns the current state, not the full history) and input=None (it resumes from the last checkpoint, not necessarily the crash point).
MC3 — B. The distinction matters in interviews and in code reviews. Static breakpoints are configuration-time decisions: always pause before node X. interrupt() is a runtime decision inside the node: pause here if condition Y. Production HITL almost always uses interrupt() because the pause logic depends on the content (the generated plan, the PR description, the review result). Static breakpoints are development tools for graph inspection. Options A, C, and D all collapse a meaningful distinction.
MC4 — C. Two traps, both freshness-related: create_react_agent is gone in v1.x, and config["configurable"] for application context was deprecated in v0.6 and replaced by context_schema + Runtime. Option B misses the second trap. Option D overstates: langgraph.prebuilt still has valid exports (ToolNode, tools_condition, InjectedState) — the issue is specifically create_react_agent.
MC5 — B. The five criteria for “do not use LangGraph” are all present: sequential (no parallelism), no branching, no HITL, no cycles, and stateless (no crash recovery needed). Adding a StateGraph is pure overhead. Even with durability="exit" or the Functional API (options C and D), you are still paying the Pregel superstep tax and the checkpointing infrastructure for no benefit. The golden rule applies: if you are not going to use persistence, HITL, cycles, or parallelism, do not use LangGraph.
Traps to Avoid
Trap 1 (Freshness #1 — the most common interview mistake in 2026): Citing create_react_agent from langgraph.prebuilt as the current way to create an agent. It was deprecated when LangGraph 1.0 shipped (October 22, 2025) and removed in 2.0. The v1.x answer is from langchain.agents import create_agent. Note: ToolNode and tools_condition are still in langgraph.prebuilt and are correct — only create_react_agent is gone.
Trap 2 (Freshness #2 — the most dangerous production bug): Ignoring the interrupt() re-execution contract. When Command(resume=...) is called, the node runs from its first line, not from the line after interrupt(). Any side effect before the interrupt() call — writing a file, calling an external API, incrementing a database counter — fires again. In a well-designed Forge, the plan-approval node reads state (idempotent), calls interrupt(), and only performs writes after resuming. Getting this wrong produces silent duplicate side effects that are very hard to debug in production.
Trap 3 (Conceptual — CA6 vs. CA10 confusion): Confusing the Store and the checkpointer. The checkpointer is per-thread (thread_id): state from one Forge run is invisible from another run. The Store uses (namespace, key) addressing and persists across threads, across users, and across runs. Putting long-term knowledge (Tally coding conventions, past bug patterns) in the checkpointer means it disappears at the end of the thread. Putting run-scoped state (current plan, current test results) in the Store means it persists across runs when it should not. Know which one holds which kind of data.
Key Takeaways
- Forge is production-ready when all five checklist categories are checked — not when the labs pass. Labs mock what production exposes.
- The self-diagnostic grid (CA1–CA14 → primary module) tells you exactly what to re-read. Trust the primary module more than the reinforcements alone.
- The ten v0→v1 freshness traps — especially
create_react_agent,NodeInterrupt,checkpoint_during, andSendfromlanggraph.constants— are the most common interview questions on LangGraph in 2026. Know them in both directions. - LangGraph is not the answer to everything. A single LLM call, a linear pipeline, a stateless transform — none of these need a graph. The golden rule: no persistence, HITL, cycles, or parallelism means no LangGraph.
- The
interrupt()re-execution contract is the most dangerous production trap: the node re-runs from the beginning on resume. Design everything before theinterrupt()call to be idempotent. - Your portfolio artifact — Forge v1.0 deployed, with LangSmith tracing and a CI eval gate, on LangGraph v1.x — is the credential this course produces. It is more specific and more verifiable than a badge.
- Next step depends on your goal: LangGraph Platform advanced features for scale, PydanticAI for strict type safety, smolagents for lightweight HuggingFace-stack agents.
What Comes Next
This is Module 16 of LangGraph in Production, a free 16-module course that takes you from your first StateGraph to a deployed, durable autonomous coding agent on LangGraph v1.x. Start at Module 1 or browse the full syllabus.
You have finished the course. There is no Module 17.
What there is: a repo at github.com/dupuis1212/langgraph-prod-labs with the full Forge codebase at tag v1.0 — every module’s code, cumulative, with the M15 eval suite and CI gate. Fork it, extend it, use it as a reference.
And if you want to go deeper into the ecosystem:
- LangGraph Platform advanced: https://langchain-ai.github.io/langgraph/cloud/
- Course 3 — smolagents: coming soon — browse the catalogue
- Course 5 — PydanticAI: coming soon — browse the catalogue
Want the full LangGraph mastery question bank (interview-style) and news of future courses in your inbox? Subscribe here — it’s free, like everything in this series.
References
- LangGraph v1.x official documentation — https://langchain-ai.github.io/langgraph/ (as of June 2026)
- LangGraph Platform / Agent Server documentation — https://langchain-ai.github.io/langgraph/cloud/ (as of June 2026)
- LangSmith evaluation documentation — https://docs.smith.langchain.com/evaluation (as of June 2026)
openevalsandagentevalspackages — https://github.com/langchain-ai/openevals and https://github.com/langchain-ai/agentevals (as of June 2026)- Forge v1.0 lab repository — https://github.com/dupuis1212/langgraph-prod-labs (tag
v1.0) - CrewAI documentation — https://docs.crewai.com/ (as of June 2026)
- AutoGen documentation — https://microsoft.github.io/autogen/ (as of June 2026)
- PydanticAI documentation — https://ai.pydantic.dev/ (as of June 2026)
- smolagents documentation — https://huggingface.co/docs/smolagents/ (as of June 2026)