Note

Architecture & Contracts: From Product Requirements to Verifiable Technical Decisions

Oct 26, 2026

Compare architecture and technology choices against product constraints, verify AI proposals, and define contracts, permissions, failures, and experiments that guide design and implementation.

Context Product definition establishes whom the product serves, the task it completes, and how to check it. The next step turns those decisions into system responsibilities, data flows, and interaction agreements that design, implementation, and validation can share.

This stage asks three questions: How is responsibility divided, how do the parts cooperate, and what happens when something goes wrong? Its outputs can be brief: a structure diagram, one core journey, explicit contracts, and the reasons for important decisions. Adjust detail to the project. Prototypes and technical experiments can also change these decisions.

AI can help compare approaches and draft documents. The final choice needs to fit product requirements, existing capabilities, operating conditions, and the maintenance budget. Framework names, popularity, and ease of generating code provide only part of the evidence. Check direction-changing claims against official documentation for the relevant version, then investigate them with a small experiment.

The sections below move from constraints and options to responsibilities, contracts, permissions, and failures, then validation and handoff. Prompts appear where they are used; replace the [placeholders] with your information.


1. Extract Technical Constraints from the Product Definition

Identify Conditions That Could Change the Approach

Start with the PRD's core journey, acceptance criteria, and known constraints. Translate broad intentions into conditions that affect implementation. Separate business requirements, provisional targets, and measured results.

Product conditionArchitectural questionHow to establish it
People enter text, files, or mediaWho retrieves and parses it, what sizes are allowed, and what may reach external services?Representative input and data use requirements
People wait for a resultCan the call return synchronously, or does it need task queries or streamed feedback?Waiting targets, measured duration, and hosting limits
Work must resume later or across devicesWhich state must persist, for how long, and how is the user identified?Specific recovery scenarios and access requirements
Several people act or charges are involvedWho may access each resource, and could repeated requests duplicate work or charges?Access rules and repeated-operation examples
An external API is requiredDo quotas, failures, costs, or data processing terms affect the journey?Version-specific documentation and a small real call

The knowledge practice tool from the previous chapter initially tests a single session. Questions and answers may stay on the current page while the server generates and validates the exercise. Saved history needs its own justification. If reviewing across sessions becomes central, revisit persistence. Even without history, specify how temporary state, logs, and external services handle the material.

Test Direction-Changing Uncertainty First

Record confirmed conditions, provisional targets, and questions still to test separately. Input availability, a critical platform capability, or an execution limit may require an early experiment. Folder structure and module naming can become more detailed once the approach is clear.

An experiment should answer one important question. For example, run representative material through generation, record time, cost, and failure behavior, then judge whether a synchronous request fits the scenario. Keep unmeasured metrics as targets rather than established capabilities.


2. Compare Architecture and Technology Choices Against Constraints

Separate System Structure from Tool Names

Client frameworks, server frameworks, databases, managed services, and editors solve different problems and can be combined. Taro and uni-app mainly affect the client; FastAPI and NestJS mainly support server interfaces; VS Code and HBuilderX affect the development workflow. Choosing a client framework does not automatically choose a server language.

First decide which responsibilities can live in one application, which need to run independently, and which can use managed capabilities. For existing products, understand the current structure and compatibility requirements. Independent services, a shared backend for multiple clients, and microservices each need a concrete reason, including deployment, troubleshooting, and maintenance costs.

Common Candidates and Where to Consider Them

These combinations are examples for comparison, with no fixed ranking. Product context, team experience, and validation of critical capabilities determine the choice.

SituationCandidate combinationExample and main checks
Websites, content platforms, and small web productsNext.js + TypeScript, adding data and managed services as neededPortfolios, member content sites, and booking tools; check rendering, server responsibilities, and operating limits. Next.js documentation
An existing frontend needs an independent business serviceReact / Vue + NestJSAdmin tools or several clients sharing business interfaces; justify the separate service and maintenance work. NestJS documentation
Core processing depends on PythonA web client + FastAPIDocument processing, data analysis, and model services; check Python dependencies, duration, and resource needs. FastAPI documentation
The product explicitly targets WeChat aloneA native WeChat Mini ProgramLocal services or workflows inside WeChat; check platform capabilities, publishing requirements, and actual cross-platform demand
The team wants to reuse React / Vue experience across platformsTaro + a server chosen for the business taskA Mini Program first, with other clients later; verify the combination of framework version, component library, and target platform. Official Taro repository
The team wants to use Vue for a cross-platform productuni-app + a server chosen for the business taskForms, content, and booking products; check compilation, plugins, and each target's run and debug workflow. uni-app documentation

Include existing Java, .NET, or other established systems when relevant. Add databases, queues, and caches for actual persistence, consistency, or execution requirements. Calling an AI API does not itself require Python or an independent backend.

Example: Check an AI Recommendation About Mini Program Tooling

Suppose a project starts with a WeChat Mini Program, may later support H5, and wants to keep using VS Code. After AI suggests uni-app, the developer asks: Which steps work with the existing editor and CLI, and which require dedicated tools?

Check editing, compilation, running and debugging, and packaging separately. Official uni-app documentation supports using editors such as VS Code for CLI projects, while App running and debugging still involves HBuilderX tooling. Verify Mini Program, H5, and App workflows individually, and distinguish uni-app from uni-app x. Official CLI and tooling guidance

Then use the candidates to run a page, one critical platform capability, and a real API call. Record tools and adaptation work. A developer with stronger React experience may choose Taro after the target-platform experiment succeeds; prior Vue and uni-app experience may lead elsewhere. This illustrates a conditional decision, not a completed project benchmark. Cross-platform support still requires adaptation and testing on devices.

Ask AI to Compare Options and Expose Evidence Gaps


3. Define Responsibilities and Trace the Core Journey

Draw Enough to Explain Important Decisions

Begin with the system, users, and external services. Expand applications, data stores, and independently running parts where helpful. Label what each connection carries, who initiates it, and which boundary it crosses. Use C4 system context and container diagrams at the level that explains the current design. A C4 container is a running or storage unit, not specifically a Docker container.

The knowledge practice tool could begin with this arrangement. It continues the hypothetical product from the previous chapter and still needs technical experiments and user feedback.

PartResponsibility in this versionInteraction boundary
Client pageCollect material, show processing state, handle answers and the recapSubmit material and consume structured results without holding model-service secrets
Application serverValidate input, call generation, validate output, return the agreed resultMay belong to the same full-stack application as the page
External generation serviceGenerate candidate questions from the materialIts output is checked by the application; establish transfer and retention conditions
Necessary temporary runtime stateSupport request handling and explicit operating controlsDefine retention for runtime needs without automatically introducing user history

Trace an Action to an Observable Result

Paste material → Client sends a request → Server validates it → Call generation → Check structure and required constraints → Return the exercise → Answer and review

Follow input, processing, state, results, and failure paths along this journey. Record observable states such as not started, processing, ready to answer, and failed. Decide which belong to the interface and which must be stored on the server.

If experiments show that synchronous processing fits the waiting target and operating limits, it can be the initial approach. If work must run longer, resume after leaving, or be queried across devices, consider task creation and result queries or another suitable interaction. When introducing task states, define allowed transitions, terminal states, and result retention together.


4. Write Explicit Interaction Contracts

Agree on Structure, Meaning, and Behavior

A contract is the shared agreement on inputs, outputs, and behavior. Describe HTTP APIs with OpenAPI where appropriate. Internal functions can use types with runtime validation; events also need message structures and handling rules. Language-level types need to be paired with checks on data actually received.

A successful JSON response is one example. A contract should also specify:

  • The trigger, caller, access rules, and applicable version.
  • Field types, required fields, nullability, units, limits, and enums.
  • Success and failure structures, waiting budgets, and retry behavior.
  • Repeated requests, state changes, compatibility, and representative examples.

Example: A Synchronous Contract for One Practice Session

This example keeps the single-session scope and assumes an experiment permits a synchronous response. Numbers and time budgets are illustrative decisions, not measurements. Change the approach if hosting limits or experiments do not support it.

ItemExample agreement
OperationPOST /api/practice-sessions with a JSON body containing the required string material
Input boundaryTrim leading and trailing whitespace, count Unicode code points, and accept 200–2,000 characters; validate again on the server
SuccessHTTP 200 with an object containing exactly three items in questions; question IDs are unique within the session
Question structureA question, four choices with unique IDs, one valid correct-choice ID, an explanation, and a source quote; all required and nonempty
Output constraintsThe correct-choice ID belongs to that question's choices. The source supports the answer and explanation, with content checks in addition to structural validation
Waiting and retryAn illustrative 20-second budget for the server's external call, checked against hosting limits before release. Prevent repeated interface submission while processing and offer explicit retry after failure
Retention boundaryKeep the exercise and answers on the current page without promising recovery after closing. Retry may generate a new result and incur another API cost

questions and choices are arrays containing question and choice objects. Question fields id, prompt, correctChoiceId, explanation, and sourceQuote, and choice fields id and text, are nonempty strings.

The object below shows one question, not the complete response, which still requires three. The source sentence, "The event starts every Wednesday at seven in the evening," supplies the answer.

{
  "id": "q1",
  "prompt": "On which day is the event held?",
  "choices": [
    { "id": "a", "text": "Tuesday" },
    { "id": "b", "text": "Wednesday" },
    { "id": "c", "text": "Thursday" },
    { "id": "d", "text": "Friday" }
  ],
  "correctChoiceId": "b",
  "explanation": "The source states that the event takes place every Wednesday.",
  "sourceQuote": "The event starts every Wednesday at seven in the evening."
}

This is personal self-assessment, allowing the client to receive answers and show feedback. Formal assessment or secret answers require a different boundary for returning answers and scoring on the server. When material cannot support the required questions, return a recognizable failure rather than inventing content to reach the count.

Ask AI to Draft a Contract


5. Agree on Permissions and Failure Behavior

Make Trust Boundaries Explicit

Describe identifying a caller separately from granting access to a resource. For user resources, check authorization on the trusted server for each operation. Showing or hiding a client button is an interface choice. Anonymous features also need explicit access scope and controls on resource consumption.

Keep model secrets, protected business credentials, and charging decisions on the trusted server. Validate both client input and externally generated output. Document which services receive material and how logs and data are retained. Configure CORS for actual browser origins where applicable; it does not check identity or resource permissions. See the OWASP REST security guidance.

Turn Failures into Predictable Interaction Results

The practice tool could use the following error agreements. Define codes consistently within the project.

SituationExample HTTP status and application codeCaller behavior
Input type or length is invalid400 / INVALID_INPUTPreserve the material and explain the required change
Material cannot support the questions422 / INSUFFICIENT_MATERIALExplain why and allow more material
A usage limit is reached429 / RATE_LIMITEDExplain when to retry and follow server-provided waiting information
The upstream call fails or output violates the contract502 / UPSTREAM_FAILURE or INVALID_OUTPUTPreserve input and offer explicit retry where the error allows it
Generation exceeds its waiting budget504 / GENERATION_TIMEOUTEnd the current wait and offer retry without assuming upstream work has been cancelled

A common failure response can contain error.code, a displayable error.message, error.retryable, and a diagnostic requestId. Do not expose secrets, raw stack traces, or internal service details. The client also needs behavior for network and hosting failures outside the application's control: preserving input, ending the wait, and offering recovery.

Agree separately on whether retries can duplicate writes or charges. For operations that must not repeat, consider an idempotency identifier, deduplication scope, expiry, and concurrency behavior. Disabling repeated clicks does not guarantee one server execution. The synchronous example permits explicit regeneration, so explain that it may produce a new result and another API cost.


6. Check the Approach with Mocks and Small Experiments

Generate Examples from the Same Contract

Mocks simulate interactions so interface and server work can advance against a shared agreement. Prepare relevant cases such as success, invalid input, processing, timeout, and external failure, keeping field and state meanings consistent.

Browser and Node.js projects can use MSW to simulate network interactions. Other environments may use suitable platform stubs or test services. Choose tools for the contract; clients do not all need the same interception mechanism.

Mocks check whether a consumer handles agreed results. Real interfaces also need checks that their actual responses match the contract. Missing fields, nulls, error codes, and version changes can reveal drift. Structural checks on AI output do not establish that answers are supported by the source material.

Keep Real Validation for Important Risks

For the practice tool, call the real generation path with a small set of reviewed representative materials. Record whether it returns three valid questions, their source support, time, and cost. Simulate upstream failure and invalid output to check errors and input recovery. Validate the target runtime as well as local behavior.

For a Mini Program choice, check compilation, preview, critical platform APIs, and behavior on devices. When an experiment fails, revise the architecture and related contracts. The result may call for narrower input, a different waiting model, or another technical approach.


7. Record Important Architecture Decisions

Preserve Reasons, Costs, and Reconsideration Conditions

Use a short Architecture Decision Record (ADR) for decisions that affect system boundaries, data, compatibility, or operation. It preserves the context, rationale, and consequences of a decision so another developer or a fresh AI conversation can recover the reasoning.

For example, a decision to start with synchronous generation can state that the current scope tests one session and acceptance depends on an experiment meeting the waiting budget in the target environment. It accepts explicit retry after failure and reopens task-based processing if duration, recovery needs, or platform limits change. Keep the status Proposed until the experiment is complete.

Update technology choices alongside decisions. When a contract changes, check consumers, mocks, and acceptance examples together. Retaining an old interface, introducing a version, or coordinating an upgrade depends on compatibility needs. Across languages, people, or multiple AI tasks, identify the same contract as the current reference.


8. Review and Hand Over to the Next Stage

Check Alignment Across Requirements, Structure, and Contracts

Hand Over a Usable Current Version

Provide the current PRD, structure and journey descriptions, contracts and examples, important decisions, and open questions. For each critical unknown, state its impact, validation method, and when a decision is needed. For existing architecture, identify the change and behavior that must remain compatible.

The UI prototype can use this material for input limits, waiting, failure, retry, and result states; implementation can build and validate the agreed behavior. If platform or runtime uncertainty could make the core journey infeasible, complete the necessary experiment first. Update product definition if it changes value or scope.

Detailed deployment, storage configuration, and framework installation can remain in later chapters. Architecture and contracts continue to evolve during implementation, providing a clear, verifiable approach the product can afford to maintain.

← Back to Notes