A solution design document (SDD) is the short, written plan a team agrees on before it builds. It says what problem is being solved, how success will be measured, which options were compared, what the design is, what could go wrong and what it will cost.
For AI, the document has extra work to do. A model can be wrong, so the plan must say who checks its answers and how the team will test quality. A model is billed per use and can be retired by its provider, so the plan must name the cost and the replacement path. And the AI touches company data, so it must say whose permissions apply.
Writing it takes days, not weeks, and it is the cheapest point to find a flaw: a sentence is easier to change than a deployed service.
AI pilots often stall on things nobody wrote down: no baseline to prove improvement, a late security review, an unestimated cost, or a model that disappears from the catalog.
Take the course's running example, blocked sales orders in order-to-cash. Over this unit, the team built an AI API that explains why an order is blocked, a CAP extension that clerks use, and a deployment on SAP BTP. Before that work goes in front of the order desk, a design document answers questions the business will ask anyway:
Is it worth it? "Cut the time a clerk needs to understand a block from 12 minutes to 4" can be measured. "Make the order desk more efficient" can't.
Why not a standard feature? The document shows the options compared, including SAP's own, and why one was chosen. That stops the same debate from restarting every quarter.
Is it safe? It names the data sent to the model, whose SAP permissions apply, and who checks the AI's answer before anyone acts on it.
What will it cost to run? Model calls on SAP's generative AI hub are metered per token. The document turns expected volume into a monthly figure the budget owner can sign.
Who owns it after go-live? It names the on-call owner, the runbook and how the team will react when a model is retired.
It also protects the approvers: months later, the decisions and their reasons are on record.
SAP doesn't sell a "design document" product. As of October 2026, it offers several ingredients that a good AI design document draws on:
SAP Activate. SAP's implementation method runs in six phases: Discover, Prepare, Explore, Realize, Deploy and Run. In Explore, teams run fit-to-standard workshops to check what standard SAP already covers, and record gaps in a prioritized backlog. An AI design document belongs here: after the standard has been checked, before the Realize sprints build anything.
SAP Architecture Center. SAP publishes reference architectures and an "AI golden path" for building generative AI applications on SAP BTP. It covers building, deploying and running such apps, including evaluation, logging and planning for model retirement. Starting from a published reference architecture saves the team from designing the basics again.
SAP BTP solution diagram guideline. SAP publishes diagram templates for draw.io, so architecture pictures look the same across teams and partners.
Clean core levels. SAP rates extensions from level A (built only on released APIs) to level D (not clean core). The design should state its level, because it decides how safely the system can be upgraded.
Responsible AI. SAP's own AI ethics handbook, written for SAP employees, sorts AI use cases into standard, high-risk and red line. Personal data, automated decisions or use in areas such as HR can make a use case high-risk, which brings extra review. Customers can borrow the same approach for their own designs.
Model rates and retirement dates. SAP's documentation points to SAP Note 3437766 for the models available in the generative AI hub, their token conversion rates and their deprecation dates. A design that depends on a model should cite it.
Use this table to review a document someone hands you. Each section answers one question; if the answer is missing, ask for it before approving the build.
Section
The question it answers
Who should care most
Problem and success metric
What will be better, by how much, and how will we know?
Process owner, sponsor
Scope and non-goals
What will this release not do?
Everyone, to stop scope creep
Options considered
Did we check standard SAP, and why build instead?
Enterprise architect, sponsor
Architecture
What are the parts and how do they connect?
Architects, IT operations
Data and integration
Which SAP data, through which APIs, at which clean core level?
SAP basis, data protection
AI design
Which model, which prompt version, what happens when it fails?
Tech lead
Security and authorizations
Who can call it, and whose SAP permissions apply?
Security
Responsible AI
Which risk class? Who can overrule the AI? Do users know it's AI?
Compliance, works council where relevant
Evaluation plan
How many test cases, and what score allows go-live?
Process owner, tech lead
Operations
Who is on call, where is the runbook, how do we swap a retired model?
IT operations
Cost estimate
What does a month of use cost, against what value?
Budget owner
Risks
What could go wrong, how likely, and who owns the fix?
Sponsor
Decisions
What did we decide, and why?
Future team members
Approvals
Who signed, and when?
Everyone
For a use case like blocked orders, expect about 4 to 10 pages.
"Design documents are for waterfall projects." A design document records decisions and reasons; it doesn't fix a project plan. Agile teams use them to agree before a sprint builds the wrong thing.
"The vendor's reference architecture is our design." A reference architecture shows a pattern. Your design adds your metric, your data, your permissions, your cost and your risks.
"We'll measure success after go-live." Without a baseline taken before the change, there is nothing to compare against.
"AI cost is a licence question only." Generative AI on SAP BTP is metered per use, so cost grows with volume and prompt length. The design must estimate it.
"Once approved, the document is done." It changes as the build teaches the team things. New decisions are added; old ones are marked as replaced, not deleted.
"The model choice is the big decision." Models change often. Where the AI lives, which data it sees and who checks its answers usually matter more and last longer.
Pick one answer for each question. The explanation appears after you choose.
1A sponsor asks why the team should write a design document before building the blocked-order explainer. What is the strongest reason?
Answer: B. A sentence is cheaper to change than a deployed service, so problems found in the document cost least. SAP doesn't require a document to use BTP, and the document plans testing rather than replacing it.
2Which success statement belongs in a good AI design document?
Answer: C. A useful metric has a measured baseline, a target and a date, so improvement can be proven. "More efficient" can't be measured, and the model choice is a means, not the goal.
3Where does an AI design document fit in SAP Activate?
Answer: A. Explore is where fit-to-standard shows what the standard covers and gaps go into the backlog. The design document then plans the gap's solution before Realize builds it.
4A design document for an HR screening assistant marks the use case as "standard" risk. What should a reviewer do?
Answer: D. SAP's ethics approach treats personal data, automated decisions and areas such as HR as high-risk triggers, which bring extra review. A different model doesn't change the use case's risk.
5The team says the generative AI hub "is already licensed", so the document needs no cost section. What is wrong with that?
Answer: B. SAP meters generative AI in tokens that convert to capacity units, so each call has a cost that rises with volume and prompt length. The budget owner needs a monthly figure before approving.
6The model the design depends on gets a deprecation date. What should the document already contain?
Answer: D. Models get retired, so the operations section should name who watches deprecation dates (SAP Note 3437766 lists them) and how a replacement is tested before switching. In-house training is rarely the answer.
7A partner presents SAP's reference architecture slide as their solution design. What is missing?
Answer: C. A reference architecture is a reusable pattern. The design must apply it to your process, with your data, your security, your numbers and the decisions you made.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Deep layer · 35 min read
#Mental model: a design document is a list of decisions you can still change cheaply
A design document is not a description of code. It is a set of decisions, each with its reasons, written early enough that a reviewer can change them with a comment instead of a rewrite. Malte Ubl's account of design docs at Google puts the emphasis on trade-offs: if a design has none, it probably didn't need a document.
For an AI use case, three kinds of decision need more care than in ordinary software:
How you will know it works. Model output varies, so "it passed the demo" proves nothing. The document fixes a test set and a pass threshold before the build, while nobody has an incentive to lower the bar.
What happens when it's wrong. Every AI feature is sometimes wrong. The document says who checks the output, what the fallback is, and the worst outcome if a wrong answer is acted on.
What it depends on that can change. The model, its price and its availability belong to someone else. The document names the dependency and the plan for when it changes.
Everything else, from the diagram to the cost table, supports those decisions.
A design document moves through a few stages. Ubl describes them as writing and fast iteration with close colleagues, a wider review, updates during implementation, and later use as a record for new team members.
flowchart LR
D[Draft with<br/>close team] --> R[Review by<br/>owners and experts]
R -->|changes| D
R --> A[Approved]
A --> B[Build in sprints]
B -->|new decision| N[Add an ADR]
N --> B
B --> L[Living record<br/>after go-live]
Two rules keep it useful. First, update it when the build teaches you something; a document that disagrees with the system is worse than none. Second, keep decisions append-only: you add new records and mark old ones as replaced, so the history stays readable.
The general shape of a design document is common: context and scope, goals and non-goals, the design, alternatives considered, and cross-cutting concerns such as security and privacy. The course template keeps that shape and adds sections AI work needs:
Section
What it must contain
Why AI makes it harder
Problem and success metric
Metric, baseline with method, target with date
Without a baseline, a fluent model looks like an improvement even when it isn't
Options considered
At least one standard SAP option, and the chosen one marked
SAP ships AI features each release; the check goes stale
Data and integration
APIs, fields sent to the model, clean core level
Prompts carry business data out of the system
AI design
Model, prompt version, output format, fallback
The model can fail, time out or be retired
Security and authorizations
Callers, whose SAP permissions apply, where secrets live
An AI layer can see more than the user it serves
Responsible AI
Risk class, personal data, human oversight, transparency
Wrong answers can harm people, not just processes
Evaluation plan
Test set size and source, pass threshold, monitoring
Quality is statistical, so it needs a number
Operations
Owner, runbook, model lifecycle
Model retirement is a scheduled outage you must plan for
Some decisions deserve their own short record. The architecture decision record (ADR) format, introduced by Michael Nygard, has a title, a date, a status, the context, the decision and its consequences. Status is one of proposed, accepted, rejected, deprecated or superseded. Consequences list the good, the neutral and the bad.
ADRs are not edited after acceptance. When a decision changes, you write a new ADR, mark the old one as superseded and link the two. That keeps the reasoning of the time visible, which is exactly what a new team member or an auditor needs.
Typical ADRs for an SAP AI use case: where the AI lives (embedded, Joule, side by side on BTP, or outside SAP), whether user permissions are passed through to SAP, which fallback to use, and whether to call the model directly or through SAP's orchestration service.
A design needs one context diagram: users, the new parts, the SAP systems and the model, with arrows for calls. A mermaid flowchart in the document is enough for review. For diagrams that go to customers or architecture boards, SAP's BTP solution diagram guideline offers official templates for draw.io, so shapes and colors match what SAP architects use.
Ubl suggests 10 to 20 pages for large projects and 1 to 3 pages for small, incremental changes. He also says to skip the document when the solution is obvious and nothing needs a trade-off. A single AI use case on an existing platform usually lands in between: 4 to 10 pages, plus ADRs.
#Build it yourself: write and check a design document for blocked orders
You will create a design document from a template, compare it with a filled example for the blocked-order explainer, and run a small checker. The checker finds missing sections, leftover placeholders, a missing baseline, risks without owners and incomplete decision records. It also turns your volume and rate inputs into a monthly cost. Optionally, a model reviews the document and lists its five biggest gaps.
flowchart LR
T[--new<br/>blank template] --> D[solution_design.md<br/>you write it]
S[--sample<br/>filled example] -.compare.-> D
D --> C[sdd_check.py<br/>checks + cost]
C -->|--llm, optional| O[SAP orchestration<br/>service: review]
Before you start: complete Set up your computer for this course and Set up for Unit 6. They create your orchestrate-course folder with its .venv. For the optional model review (Step 6) you also need Set up for Unit 5, which puts the AICORE_ lines in .env and installs sap-ai-sdk-gen. This walkthrough doesn't repeat those steps.
In VS Code's file list, right-click unit06, choose New File, name it sdd_check.py, paste the code below and save.
"""Write, check and cost a solution design document (SDD) for an AI use case.
python unit06/sdd_check.py --new unit06/solution_design.md write a blank template
python unit06/sdd_check.py --sample unit06/sample_design.md write a filled example
python unit06/sdd_check.py unit06/sample_design.md check a document and estimate cost
python unit06/sdd_check.py unit06/sample_design.md --llm also ask a model for a review
--llm needs the AICORE_ lines in .env from "Set up for Unit 5". Everything else needs only Python.
"""
import argparse
import re
import sys
from pathlib import Path
FENCE = "`" * 3 # three backticks, built here so this file can sit inside a markdown page
SECTIONS = ["Summary", "Problem and success metric", "Scope and non-goals", "Users and process",
"Options considered", "Architecture", "Data and integration", "AI design",
"Security and authorizations", "Responsible AI", "Evaluation plan", "Operations",
"Cost estimate", "Risks", "Decisions", "Open questions", "Approvals"]
ADR_STATUSES = {"proposed", "accepted", "rejected", "deprecated", "superseded"}
RISK_CLASSES = {"standard", "high-risk", "red line"}
TEMPLATE = f"""# Solution design: TODO name of the use case
Version: 0.1 (draft) · Author: TODO · Last changed: TODO date
## Summary
TODO: three sentences. The problem, the proposed AI solution, the number it should move.
## Problem and success metric
TODO: who has the problem today, in which SAP process, and what it costs.
- Metric: TODO what you will measure
- Baseline: TODO today's value, with a number and how you measured it
- Target: TODO the value you promise, with a number and a date
- Measured by: TODO where the number comes from (report, log, sample)
## Scope and non-goals
In scope:
- TODO
Non-goals:
- TODO what this release will not do, so nobody expects it
## Users and process
TODO: who uses it, at which step of the process, and what they do with the answer.
## Options considered
| Option | Pros | Cons | Verdict |
| --- | --- | --- | --- |
| TODO standard SAP feature | TODO | TODO | TODO write chosen or rejected |
| TODO your own build | TODO | TODO | TODO write chosen or rejected |
## Architecture
TODO: one paragraph, then a diagram.
{FENCE}mermaid
flowchart LR
U[TODO user] --> A[TODO app]
A --> S[TODO SAP system]
{FENCE}
## Data and integration
- SAP APIs used: TODO name each API and whether it is released
- Data sent to the model: TODO fields, and whether any are personal data
- Clean core level: TODO A, B, C or D
## AI design
- Model: TODO
- Prompt version: TODO
- Output format: TODO
- Fallback: TODO what happens when the model fails or times out
## Security and authorizations
- Who may call: TODO
- Whose authorizations apply to SAP data: TODO
- Secrets: TODO where keys live (never in this document)
## Responsible AI
- Risk class: TODO standard, high-risk or red line
- Personal data: TODO
- Human oversight: TODO who checks or can overrule the AI
- Transparency: TODO how users know an answer came from AI
## Evaluation plan
- Test set: TODO how many cases, from where
- Pass threshold: TODO the score that allows go-live
- Monitoring after go-live: TODO
## Operations
- Owner: TODO the person or team on call
- Runbook: TODO link
- Model lifecycle: TODO how you learn about model retirement and switch
## Cost estimate
| Input | Value |
| --- | --- |
| Requests per working day | TODO |
| Working days per month | TODO |
| Input tokens per request | TODO |
| Output tokens per request | TODO |
| Price per 1M input tokens | TODO from your rate card |
| Price per 1M output tokens | TODO from your rate card |
| Runtime cost per month | TODO |
| Currency | TODO |
| Minutes saved per request | TODO optional |
| Staff cost per hour | TODO optional |
## Risks
| Risk | Likelihood | Impact | Mitigation | Owner |
| --- | --- | --- | --- | --- |
| TODO | TODO | TODO | TODO | TODO |
## Decisions
### ADR-1: TODO title
- Status: TODO proposed, accepted, rejected, deprecated or superseded
- Context: TODO
- Decision: TODO
- Consequences: TODO
## Open questions
- TODO
## Approvals
| Role | Name | Date |
| --- | --- | --- |
| Process owner | TODO | |
| Security | TODO | |
"""
SAMPLE = f"""# Solution design: blocked-order explainer
Version: 0.3 (draft) · Author: course learner · Last changed: 2026-10-02
> Example for a made-up company. Every number, name and price in it is invented for practice.
## Summary
Clerks at the order desk spend too long finding out why a sales order is blocked. An AI service reads
the order through released SAP APIs and explains the block in plain words, with a suggested next step.
The goal is to cut the time a clerk needs to understand a block from 12 minutes to 4.
## Problem and success metric
About 400 sales orders a day stop on a delivery or credit block. A clerk opens several screens to see why,
then emails sales or credit management. Customers wait and clerks lose most of a morning.
- Metric: median clerk minutes to understand why an order is blocked
- Baseline: 12 minutes (timed sample of 60 orders, two weeks in September)
- Target: 4 minutes by end of Q1, with no drop in correct next steps
- Measured by: the same timed sample, repeated after four weeks of use
## Scope and non-goals
In scope:
- Explaining delivery and credit blocks on standard sales orders
- Suggesting one next step from a fixed list
Non-goals:
- Releasing or changing any order (only people with the right SAP role do that)
- Returns, consignment and intercompany orders
## Users and process
Order-desk clerks in order-to-cash, at the step where they work through the list of blocked orders.
They read the explanation, check it against the order, then act in SAP as they do today.
## Options considered
| Option | Pros | Cons | Verdict |
| --- | --- | --- | --- |
| Standard SAP AI feature | No build; SAP maintains it | None found for this exact need in our release | rejected for now; recheck each release |
| Joule agent | Inside SAP's own user experience | Needs Joule Studio skills we don't have yet | rejected for release 1 |
| CAP extension on SAP BTP with our AI API | Clean core; reuses Unit 6 work; full control | We own operations and cost | chosen |
## Architecture
The CAP extension reads the order through a released API, calls the AI API, stores the explanation and
returns it to the clerk. The AI API calls the model through SAP's orchestration service.
{FENCE}mermaid
flowchart LR
C[Clerk] --> X[CAP extension on BTP]
X -->|released OData API| S[SAP S/4HANA]
X --> A[AI API on Cloud Foundry]
A --> O[Orchestration service]
O --> M[Model]
{FENCE}
## Data and integration
- SAP APIs used: Sales Order (A2X), released; read only
- Data sent to the model: order number, block reason, amounts, dates, customer name; no contact details
- Clean core level: A
## AI design
- Model: chosen from the generative AI hub; name recorded per release
- Prompt version: explain-v3, stored in Git with the code
- Output format: JSON with explanation and next_step, checked against a schema
- Fallback: a fixed-rule explanation from the block reason when the model fails or times out after 30 seconds
## Security and authorizations
- Who may call: clerks with the order-desk role, through company sign-in
- Whose authorizations apply to SAP data: the clerk's own, passed through to SAP
- Secrets: in a bound service on BTP; rotated every 90 days
## Responsible AI
- Risk class: standard (no automated decision; customer names are business data, contact details excluded)
- Personal data: customer names only; masked before the model call
- Human oversight: the clerk decides every action; the AI cannot change an order
- Transparency: every explanation is labelled "AI-generated, check before acting"
## Evaluation plan
- Test set: 120 past blocked orders with a correct explanation written by two senior clerks
- Pass threshold: 90% of explanations judged correct, and 100% of next steps from the allowed list
- Monitoring after go-live: clerks rate each explanation; weekly review of every "not helpful"
## Operations
- Owner: order-to-cash product team, business hours support
- Runbook: unit06/deployment_runbook.md
- Model lifecycle: check SAP Note 3437766 monthly for deprecation dates; test the replacement on the test set
## Cost estimate
| Input | Value |
| --- | --- |
| Requests per working day | 400 |
| Working days per month | 21 |
| Input tokens per request | 1,200 |
| Output tokens per request | 250 |
| Price per 1M input tokens | 0.50 (example, not a real price) |
| Price per 1M output tokens | 2.00 (example, not a real price) |
| Runtime cost per month | 150 (example) |
| Currency | EUR |
| Minutes saved per request | 8 |
| Staff cost per hour | 40 (example) |
## Risks
| Risk | Likelihood | Impact | Mitigation | Owner |
| --- | --- | --- | --- | --- |
| Explanation is wrong and a clerk acts on it | Medium | Medium | Label as AI; clerk checks; weekly review of ratings | Product owner |
| Model is retired by the provider | High | Medium | Monthly check of SAP Note 3437766; re-run test set on replacement | Tech lead |
| Token cost grows with volume | Low | Low | Monthly cost report; cache repeated orders | Tech lead |
## Decisions
### ADR-1: Build side by side on SAP BTP, not inside S/4HANA
- Status: accepted
- Context: no standard feature covers the need; we must keep the core clean
- Decision: a CAP extension that reads released APIs and calls our AI API
- Consequences: clean core level A; we own a BTP app, its cost and its on-call
### ADR-2: Keep a rule-based fallback
- Status: accepted
- Context: the model can time out or be unavailable; clerks still need an answer
- Decision: return a fixed explanation from the block reason when the model fails
- Consequences: clerks always get an answer; fallback answers are less helpful and are counted
## Open questions
- Should credit managers get a different explanation from clerks?
## Approvals
| Role | Name | Date |
| --- | --- | --- |
| Process owner | (example) Head of order desk | |
| Security | (example) BTP security lead | |
"""
# ---------- reading the document ----------
def split_sections(text: str) -> dict:
"""Map each '## ' heading (lower case) to the text below it."""
sections, current = {}, None
for line in text.splitlines():
if line.startswith("## "):
current = line[3:].strip().lower()
sections[current] = []
elif current is not None:
sections[current].append(line)
return {k: "\n".join(v) for k, v in sections.items()}
def field(section: str, name: str) -> str:
"""Return the text after '- Name:' in a section, or ''."""
match = re.search(rf"^\s*-?\s*{re.escape(name)}:\s*(.*)$", section, re.IGNORECASE | re.MULTILINE)
value = match.group(1).strip() if match else ""
return "" if value.upper().startswith("TODO") else value
def table_rows(section: str) -> list:
"""Return the data rows of the first markdown table in a section, as lists of cell texts."""
rows = [line for line in section.splitlines() if line.strip().startswith("|")]
cells = [[c.strip() for c in row.strip().strip("|").split("|")] for row in rows]
return [r for r in cells[1:] if not all(set(c) <= set("-: ") for c in r)]
def number(text: str):
"""The first number in a cell, ignoring thousands separators; None if there is none."""
match = re.search(r"\d[\d,]*(\.\d+)?", text)
return float(match.group(0).replace(",", "")) if match else None
# ---------- the checks ----------
def check(text: str) -> tuple:
"""Run every check. Returns (results, sections), where results are (level, name, message)."""
s = split_sections(text)
get = lambda name: s.get(name.lower(), "")
results = []
add = lambda level, name, msg: results.append((level, name, msg))
missing = [name for name in SECTIONS if name.lower() not in s]
add("FAIL" if missing else "OK", "Sections",
"missing: " + ", ".join(missing) if missing else f"all {len(SECTIONS)} present")
todo_lines = [i for i, line in enumerate(text.splitlines(), 1) if "TODO" in line]
add("FAIL" if todo_lines else "OK", "Placeholders",
f"{len(todo_lines)} lines still say TODO (first on line {todo_lines[0]})" if todo_lines else "none left")
secret = re.search(r"(?i)(secret|password|api[ _-]?key|token)\s*[:=]\s*\S{20,}", text)
add("FAIL" if secret else "OK", "Secrets",
"something that looks like a key is in the document; remove it and rotate it" if secret
else "no keys or passwords found in the text")
problem = get("Problem and success metric")
base, target, by = field(problem, "Baseline"), field(problem, "Target"), field(problem, "Measured by")
if number(base) is not None and number(target) is not None and by:
add("OK", "Success metric", f"from {base.split('(')[0].strip()} to {target.split(',')[0].strip()}")
else:
add("FAIL", "Success metric", "needs Baseline and Target lines with numbers, and Measured by")
scope = get("Scope and non-goals")
non_goals = scope.lower().split("non-goals:")[-1] if "non-goals:" in scope.lower() else ""
listed = re.search(r"^\s*- (?!todo)\S", non_goals, re.MULTILINE)
add("OK" if listed else "WARN", "Non-goals", "listed" if listed else "none listed; say what this release will not do")
options = table_rows(get("Options considered"))
chosen = [r for r in options if "chosen" in r[-1].lower() and "TODO" not in r[-1]]
if len(options) < 2:
add("FAIL", "Options", f"{len(options)} option(s); compare at least two, including a standard SAP one")
elif len(chosen) != 1:
add("FAIL", "Options", f"{len(chosen)} rows say 'chosen'; exactly one should")
else:
add("OK", "Options", f"{len(options)} compared, chosen: {chosen[0][0]}")
add("OK" if "mermaid" in get("Architecture") else "WARN", "Diagram",
"found" if "mermaid" in get("Architecture") else "no diagram in Architecture")
level = field(get("Data and integration"), "Clean core level").upper()[:1]
if level not in {"A", "B", "C", "D"}:
add("FAIL", "Clean core", "needs 'Clean core level: A, B, C or D'")
else:
add("OK" if level in {"A", "B"} else "WARN", "Clean core",
f"level {level}" + ("" if level in {"A", "B"} else "; explain why, and plan to move up"))
ai = get("AI design")
add("OK" if field(ai, "Fallback") else "FAIL", "Fallback",
"described" if field(ai, "Fallback") else "say what happens when the model fails")
sec = get("Security and authorizations")
gaps = [n for n in ("Who may call", "Whose authorizations apply to SAP data", "Secrets") if not field(sec, n)]
add("WARN" if gaps else "OK", "Security", "missing: " + ", ".join(gaps) if gaps else "callers, SAP authorizations and secrets covered")
rai = get("Responsible AI")
risk_class = field(rai, "Risk class").lower()
found = next((c for c in RISK_CLASSES if risk_class.startswith(c)), None)
approvals_text = get("Approvals").lower()
if not found:
add("FAIL", "Risk class", "needs 'Risk class: standard, high-risk or red line'")
elif found == "red line":
add("FAIL", "Risk class", "red line: this use case should not be built")
elif found == "high-risk" and not any(w in approvals_text for w in ("privacy", "ethics", "legal")):
add("FAIL", "Risk class", "high-risk needs a privacy, ethics or legal approver in Approvals")
else:
add("OK", "Risk class", found)
add("OK" if field(rai, "Human oversight") else "FAIL", "Human oversight",
"described" if field(rai, "Human oversight") else "say who checks or can overrule the AI")
ev = get("Evaluation plan")
ok = number(field(ev, "Test set")) is not None and number(field(ev, "Pass threshold")) is not None
add("OK" if ok else "FAIL", "Evaluation", "test set and pass threshold have numbers" if ok
else "needs 'Test set:' and 'Pass threshold:' lines with numbers")
ops = get("Operations")
gaps = [n for n in ("Owner", "Runbook", "Model lifecycle") if not field(ops, n)]
add("WARN" if gaps else "OK", "Operations", "missing: " + ", ".join(gaps) if gaps else "owner, runbook and model lifecycle named")
risks = table_rows(get("Risks"))
incomplete = [r[0] for r in risks if len(r) < 5 or not r[3] or not r[4]]
if len(risks) < 3 or incomplete:
add("FAIL", "Risks", f"{len(risks)} listed, {len(incomplete)} without mitigation or owner; aim for 3 or more, all complete")
else:
add("OK", "Risks", f"{len(risks)} listed, each with a mitigation and an owner")
adrs = re.split(r"^### ", get("Decisions"), flags=re.MULTILINE)[1:]
bad = []
for adr in adrs:
status = field(adr, "Status").lower().split()[0] if field(adr, "Status") else ""
if status not in ADR_STATUSES or not all(field(adr, n) for n in ("Context", "Decision", "Consequences")):
bad.append(adr.splitlines()[0][:40])
if not adrs or bad:
add("FAIL", "Decisions", "no decision records" if not adrs else "incomplete: " + "; ".join(bad))
else:
add("OK", "Decisions", f"{len(adrs)} recorded with status, context, decision and consequences")
dated = [r for r in table_rows(get("Approvals")) if len(r) > 2 and re.search(r"\d{4}-\d{2}-\d{2}", r[2])]
add("OK" if dated else "INFO", "Approvals", f"{len(dated)} signed" if dated else "none signed yet (still a draft)")
return results, s
def cost(section: str) -> list:
"""Turn the cost table into report lines. Prices are the reader's inputs, never SAP's."""
values = {r[0].lower(): r[1] for r in table_rows(section) if len(r) > 1}
find = lambda start: next((v for k, v in values.items() if k.startswith(start)), "")
keys = {"per_day": "requests per working day", "days": "working days per month",
"tok_in": "input tokens per request", "tok_out": "output tokens per request",
"p_in": "price per 1m input tokens", "p_out": "price per 1m output tokens",
"runtime": "runtime cost per month"}
n = {k: number(find(v)) for k, v in keys.items()}
missing = [keys[k] for k, v in n.items() if v is None]
if missing:
return ["Cost estimate skipped. Missing numbers for: " + ", ".join(missing)]
cur = find("currency") or ""
requests = n["per_day"] * n["days"]
t_in, t_out = requests * n["tok_in"], requests * n["tok_out"]
model = t_in / 1e6 * n["p_in"] + t_out / 1e6 * n["p_out"]
total = model + n["runtime"]
lines = ["Cost estimate (from your inputs; prices are yours, not SAP's):",
f" Requests per month: {requests:,.0f}",
f" Model tokens per month: {t_in + t_out:,.0f} ({t_in:,.0f} in, {t_out:,.0f} out)",
f" Model cost per month: {model:,.2f} {cur}",
f" Runtime cost per month: {n['runtime']:,.2f} {cur}",
f" Total per month: {total:,.2f} {cur}",
f" Cost per request: {total / requests:,.4f} {cur}" if requests else ""]
minutes, rate = number(find("minutes saved per request")), number(find("staff cost per hour"))
if minutes is not None and rate is not None:
value = requests * minutes / 60 * rate
lines.append(f" Time value per month: {value:,.2f} {cur} ({requests * minutes / 60:,.0f} staff hours)")
lines.append(" Time saved is not cash saved: say what the team will do with the hours.")
return [line for line in lines if line]
# ---------- optional model review ----------
REVIEW_SYSTEM = ("You review solution design documents for AI use cases on SAP as a senior solution architect. "
"List the five most important gaps or risks in the document, most serious first. For each, "
"give one line on the problem and one line on the fix. Use only what the document says; "
"if something is missing, say it is missing. Do not praise the document.")
REVIEW_USER = "<document>\n{{?doc}}\n</document>"
def llm_review(text: str, model_name: str) -> str:
"""Ask a model for a reviewer's critique through SAP's orchestration service (version 2)."""
import os
from dotenv import load_dotenv
load_dotenv()
needed = ["AICORE_CLIENT_ID", "AICORE_CLIENT_SECRET", "AICORE_AUTH_URL", "AICORE_BASE_URL",
"AICORE_RESOURCE_GROUP"]
missing = [n for n in needed if not os.environ.get(n)]
if missing:
return "Review skipped. Missing in .env: " + ", ".join(missing) + ". See 'Set up for Unit 5', Step 5."
try:
from gen_ai_hub.orchestration_v2 import (LLMModelDetails, ModuleConfig, OrchestrationConfig,
OrchestrationError, OrchestrationService,
PromptTemplatingModuleConfig, SystemMessage, Template,
UserMessage)
except ImportError:
return "Review skipped. The SAP Cloud SDK for AI isn't installed: see 'Set up for Unit 5', Step 2."
template = Template(template=[SystemMessage(content=REVIEW_SYSTEM), UserMessage(content=REVIEW_USER)])
config = OrchestrationConfig(modules=ModuleConfig(prompt_templating=PromptTemplatingModuleConfig(
prompt=template, model=LLMModelDetails(name=model_name, params={"max_tokens": 700},
timeout=60, max_retries=1))))
try:
response = OrchestrationService(config=config).run(placeholder_values={"doc": text})
except OrchestrationError as error:
return f"Review failed: orchestration error {error.code}: {error.message}"
except Exception as error: # network errors, timeouts, expired credentials
return f"Review failed: {type(error).__name__}. Run python check_unit05.py to find the cause."
return response.final_result.choices[0].message.content or "(empty answer)"
# ---------- command line ----------
def write_new(path: Path, content: str, what: str) -> None:
if path.exists():
sys.exit(f"{path} already exists. Choose another name, so your work isn't overwritten.")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8")
print(f"Wrote {what} to {path}. Open it in VS Code, then check it with:\n python unit06/sdd_check.py {path}")
def main() -> None:
parser = argparse.ArgumentParser(description="Write, check and cost a solution design document.")
parser.add_argument("doc", nargs="?", help="the design document to check")
parser.add_argument("--new", metavar="FILE", help="write a blank template to FILE")
parser.add_argument("--sample", metavar="FILE", help="write a filled example to FILE")
parser.add_argument("--llm", action="store_true", help="also ask a model for a review (needs SAP AI Core)")
parser.add_argument("--model", default="gpt-4o-mini", help="model name for --llm")
args = parser.parse_args()
if args.new:
return write_new(Path(args.new), TEMPLATE, "a blank template")
if args.sample:
return write_new(Path(args.sample), SAMPLE, "the example design")
if not args.doc:
parser.error("give a document to check, or use --new or --sample")
path = Path(args.doc)
if not path.exists():
sys.exit(f"Can't find {path}. Run from your orchestrate-course folder, or create it with --new.")
text = path.read_text(encoding="utf-8")
results, sections = check(text)
print(f"Checking {path}\n")
for level, name, message in results:
print(f"{level:<5} {name + ':':<17} {message}")
print()
for line in cost(sections.get("cost estimate", "")):
print(line)
problems = sum(1 for r in results if r[0] == "FAIL")
warnings = sum(1 for r in results if r[0] == "WARN")
print(f"\nResult: {problems} problem(s), {warnings} warning(s). "
+ ("Ready for review." if problems == 0 else "Fix the FAIL lines, then run this again."))
if args.llm:
print("\nModel review (a second opinion, not a sign-off):\n")
print(llm_review(text, args.model))
sys.exit(1 if problems else 0)
if __name__ == "__main__":
main()
Check that it starts:
python unit06/sdd_check.py --help
You should see a usage line that starts with usage: sdd_check.py and lists --new, --sample, --llm and --model.
#Step 3: Write the example and check it (no account needed)
Write the filled example for the blocked-order explainer:
Wrote the example design to unit06/sample_design.md. Open it in VS Code, then check it with:
python unit06/sdd_check.py unit06/sample_design.md
Open unit06/sample_design.md in VS Code. To see it formatted, press Ctrl+Shift+V (Windows) or Cmd+Shift+V (macOS). Read it once from top to bottom: it is about five pages, the size this topic recommends.
Checking unit06/sample_design.md
OK Sections: all 17 present
OK Placeholders: none left
OK Secrets: no keys or passwords found in the text
OK Success metric: from 12 minutes to 4 minutes by end of Q1
OK Non-goals: listed
OK Options: 3 compared, chosen: CAP extension on SAP BTP with our AI API
OK Diagram: found
OK Clean core: level A
OK Fallback: described
OK Security: callers, SAP authorizations and secrets covered
OK Risk class: standard
OK Human oversight: described
OK Evaluation: test set and pass threshold have numbers
OK Operations: owner, runbook and model lifecycle named
OK Risks: 3 listed, each with a mitigation and an owner
OK Decisions: 2 recorded with status, context, decision and consequences
INFO Approvals: none signed yet (still a draft)
Cost estimate (from your inputs; prices are yours, not SAP's):
Requests per month: 8,400
Model tokens per month: 12,180,000 (10,080,000 in, 2,100,000 out)
Model cost per month: 9.24 EUR
Runtime cost per month: 150.00 EUR
Total per month: 159.24 EUR
Cost per request: 0.0190 EUR
Time value per month: 44,800.00 EUR (1,120 staff hours)
Time saved is not cash saved: say what the team will do with the hours.
Result: 0 problem(s), 0 warning(s). Ready for review.
INFO isn't a problem: a draft has no signatures yet. "Ready for review" means the document is complete enough for people to read, not that it is right.
WARN Clean core: level C; explain why, and plan to move up
FAIL Risk class: high-risk needs a privacy, ethics or legal approver in Approvals
FAIL Risks: 3 listed, 1 without mitigation or owner; aim for 3 or more, all complete
...
Result: 2 problem(s), 1 warning(s). Fix the FAIL lines, then run this again.
Undo the three changes (Ctrl+Z or Cmd+Z in VS Code), save, and check again until it says Ready for review.
A FAIL sets exit code 1, so a pipeline can refuse to move on. A WARN is a question for the reviewer: level C can be acceptable, but the document must say why.
OK Sections: all 17 present
FAIL Placeholders: 55 lines still say TODO (first on line 1)
...
Result: 10 problem(s), 2 warning(s). Fix the FAIL lines, then run this again.
This is the valid "empty" result: every section is there and nothing is filled in. The Exercise below walks you through filling it.
Neither --new nor --sample overwrites an existing file, so you can't lose your work by running them twice.
After the usual check lines you see a section like this (the model's wording will differ):
Model review (a second opinion, not a sign-off):
1. Problem: the evaluation plan has no test for orders with several blocks at once.
Fix: add such orders to the test set and state the expected explanation.
...
If your account doesn't offer gpt-4o-mini, run python check_unit05.py to list usable models and add --model MODEL_NAME.
Treat the review as one more reviewer's comments. Some points will be wrong or already covered; decide each one yourself.
SAP Activate has six phases: Discover, Prepare, Explore, Realize, Deploy and Run. Explore runs fit-to-standard workshops, records gaps in a prioritized product backlog, and time-boxes a high-level design for the top items. Realize then builds configuration and custom extensions in sprints.
An AI use case fits that rhythm. The fit-to-standard result goes into Options considered: it is your evidence that a standard feature doesn't cover the need. The design document is the time-boxed design for that backlog item. Its ADRs grow during Realize.
The SAP Architecture Center's AI golden path for generative AI applications walks through build, deploy and run. Several of its recommendations map directly to document sections:
Golden path recommendation
Where it goes in your document
A CAP-based backend, with Destinations for SAP connectivity and role-based access
Architecture; Security and authorizations
Packaging as a multitarget application (MTA) with CI/CD
Operations
Prompts managed in the Prompt Registry rather than hard-coded
AI design (prompt version)
The Evaluation Service to test use cases across models
Evaluation plan
Logging, tracing and metering
Operations; Cost estimate
Planning for model deprecation, versioning and fallback
AI design; Operations; Risks
Start from the matching reference architecture, then write down where your design differs and why. Those differences are often your most important ADRs.
SAP's clean core model rates extensions A to D. Level A uses released APIs and extension points; B uses classic APIs; C touches internal objects not released for customers; D, not clean core, includes modifications and unsupported writes to SAP tables. An extension is rated by its lowest-ranked part, so one internal call drags the whole design down. The checker asks for the level and warns below B.
SAP's AI ethics handbook is written for SAP's own teams, but its classification is a practical model for customers. Teams classify a use case as red line (not to be built), high-risk or standard before they build. High-risk triggers include processing personal or sensitive personal data, automated decision making, and use in areas such as employment and HR, healthcare, public services and law enforcement. High-risk cases get additional review before they go ahead.
The handbook also sets expectations a design should answer: AI decisions can always be overruled by a human, through human-in-the-loop, human-on-the-loop or human-in-command oversight; users must be able to tell they are dealing with AI; and data and decision processes are documented for traceability. Your company's own policy and the law that applies to you come first; use SAP's model to structure the section, not to replace your compliance review.
The generative AI hub documentation points to SAP Note 3437766 for available models, token conversion rates, rate limits and deprecation dates. SAP AI Core's documentation (September 2026 edition) explains that generative AI is metered in tokens, input and output separately, converted to capacity units with a factor per model, and that output tokens tend to cost more than input tokens.
The same documentation shows why dated dependencies belong in the document. It lists retired models with recommended replacements, and it states that version 1 of the orchestration API is scheduled for decommissioning on 31 October 2026, with workflows to move to version 2. A design that names its model and API version, and the owner who watches these notices, turns such changes into planned work.
SAP's BTP solution diagram guideline provides templates for draw.io (with Lucidchart and PowerPoint as alternatives), based on SAP's Horizon design principles. Use it for diagrams that leave the team; keep the mermaid diagram in the document for day-to-day review.
Version and review it like code. Keep the document and ADRs in the same repository as the solution, review changes in pull requests, and tag the version that was approved. Run sdd_check.py in the pipeline so an incomplete document fails the build.
Keep secrets and customer data out. The document names where secrets live, never their values. Use made-up or masked examples. The checker's secret scan is a safety net, not a guarantee.
Classify the document itself. A design that describes permissions, data flows and weaknesses is sensitive. Store and share it like other internal architecture documents.
Trace to tests. Each promise in the document, such as the fallback or the pass threshold, should map to a test or a monitor. If nothing checks a promise, it will drift.
Re-review on change. A new model, a new data field sent to the model, a wider user group or a different risk class all need a new ADR and, often, a new sign-off.
Budget for twice the volume. Token use grows when users like the tool and when prompts get longer. Show the cost at expected and at double volume.
Clean core stays visible. If the design ever needs a level C or D part, record why, what it touches and the plan to replace it.
Writing it after the build. A design document written to describe what was built finds no flaws. Write it while choices are still open.
A metric without a baseline. "4 minutes" means nothing if nobody measured today's number the same way.
Only one option. If the document doesn't compare a standard SAP option, reviewers will ask, and the decision isn't defensible.
Copying the reference architecture. Pasting a pattern without your data, permissions and numbers gives reviewers nothing to review.
Editing old decisions. Changing an accepted ADR hides why the team acted as it did. Supersede it with a new one.
Treating the checker's "Ready for review" as approval. The script checks completeness, not truth. People still need to read it.
Leaving operations to "later". A design without an owner, a runbook and a model-retirement plan is not ready for users.
Example prices in a real document. Use your own rates; invented numbers in a signed document become a budget problem.
#Exercise: write the design document for your blocked-order explainer
You will turn your blank template into a complete design for the service you built in this unit, check it, get one review, and commit it. Unit 8 reuses its success metric and evaluation plan, and the Unit 14 capstone builds on the whole document.
Open unit06/solution_design.md (from Step 5) and unit06/sample_design.md side by side: right-click the second file's tab and choose Split Right.
Fill Summary, Problem and success metric and Scope and non-goals for your own version of the use case. If you have no real baseline, write how you would measure it (for example, "timed sample of 50 orders over two weeks") and use a clearly marked estimate.
Fill Options considered with at least two rows, one of them a standard SAP option. Write chosen in the Verdict column of exactly one row.
Fill Architecture with your own mermaid diagram, using the parts you built in this unit: the AI API, the CAP extension and the Cloud Foundry deployment.
Fill Data and integration, AI design and Security and authorizations from your code: the fields your AI API sends to the model, its fallback, its timeout, and how callers prove who they are.
Fill Responsible AI. Decide the risk class and write one sentence on why.
Fill Operations. For Runbook, write unit06/deployment_runbook.md if you did the last topic's exercise.
Fill Cost estimate with your own volume. For prices, use your company's rates or write 0.00 (unknown) and add an open question about where to get them.
Write at least three Risks, each with a mitigation and an owner, and at least two Decisions as ADRs. One ADR must record where the AI lives.
Get one review: ask a colleague or fellow learner to read it, or run Step 6 with --llm. Write each point you accept as a change, and each point you reject as a line under Open questions or a new ADR with status rejected.
Commit your work from the course folder:
git add unit06/sdd_check.py unit06/solution_design.md unit06/sample_design.md
git commit -m "Add the solution design document for the blocked-order explainer"
git status should not list .env.
Done when:python unit06/sdd_check.py unit06/solution_design.md ends with Ready for review., the document has your own baseline (or a marked estimate with a measuring plan), at least three complete risks and two ADRs, one review has been answered, and the work is committed.
Pick one answer for each question. The explanation appears after you choose.
1Why does the course template require the evaluation plan's pass threshold before the build starts?
Answer: C. Quality of model output is statistical, so it needs a number. Fixing it before results exist stops the team from moving the bar to fit what they built. The threshold isn't a training target or an SAP AI Core setting.
2Your team accepted ADR-1 ("call the model directly") last month. Now you will route calls through SAP's orchestration service. What do you do with the record?
Answer: B. ADRs are not edited once accepted. A new record captures the new decision and its context, and the old one is marked superseded with links both ways, so the earlier reasoning stays visible.
3The checker prints "WARN Clean core: level C". What does it mean for the design?
Answer: D. Level C means internal SAP objects that aren't released for customers, and an extension is rated by its lowest part. It isn't forbidden, but it carries upgrade risk the document must explain. Level A is released APIs only.
4In the checker, why does a "high-risk" class fail unless the Approvals table names a privacy, ethics or legal approver?
Answer: A. In SAP's ethics approach, high-risk cases, such as those with personal data or automated decisions, get extra review. The checker turns that into a required approver. It says nothing about which services may be used.
5You run the cost estimate and get 9.24 EUR a month for model calls. A manager wants to put that number in the budget. What should you say?
Answer: B. The checker multiplies your inputs; the example's rates are invented. Real rates come from your agreement, with model conversion factors in SAP Note 3437766. Token use grows with adoption and prompt length, so show a higher-volume case too.
6The model your design depends on gets a deprecation date. Which parts of a good document already cover this?
Answer: C. Model retirement is a planned change. The operations section names who watches SAP Note 3437766, and the risks table gives the mitigation: test the replacement on the evaluation set before switching.
7What would you do if a team lead says the design is approved because sdd_check.py printed "Ready for review"?
Answer: D. The script finds missing sections, placeholders and incomplete records. It can't tell whether the baseline is true or the design is sound. The model review is a second opinion, not an approval; the Approvals table needs real names and dates.
8Which part of the document should the SAP Architecture Center's golden path most directly shape?
Answer: C. The golden path describes how to build, deploy and run generative AI apps on BTP: CAP backend, Destinations, Prompt Registry, evaluation, logging and model lifecycle. Your metric, risk class and approvers come from your own process and company.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Sources
Design Docs at Google (Malte Ubl, Industrial Empathy)— design docs written before coding to record trade-offs; context and scope, goals and non-goals, the design, alternatives considered, cross-cutting concerns; 10-20 pages for large projects, 1-3 page mini docs; skip when the solution is obvious; update when reality diverges
SAP AI Ethics Handbook (SAP)— written for SAP employees; risk classification into red line, high-risk and standard; high-risk triggers include personal or sensitive personal data, automated decision making, and domains such as HR and healthcare; AI decisions may always be overruled by a human; AI identifiable to end users; documentation for traceability
GenAI Applications, AI golden path (SAP Architecture Center)— build, deploy and run phases; CAP-based backend, Destinations and role-based access; MTA packaging and CI/CD; Prompt Registry instead of hard-coded prompts; Evaluation Service; logging and observability; model deprecation, versioning and fallback
SAP AI Core documentation, PDF edition of 2026-09-04 (help.sap.com)— generative AI metered in GenAI tokens, input and output, converted to capacity units with model-specific factors; output tokens tend to cost more; deprecated models with replacements; orchestration API version 1 to be decommissioned on 31 October 2026
Discovering SAP Activate (learning.sap.com)— Explore runs fit-to-standard workshops, fills a prioritized product backlog and time-boxes high-level design; Realize builds configuration and extensions in sprints