Back to blog
AI Systemsintermediate

Bonus — Team Collaboration: API-First Design, OpenAPI Contracts & PR Workflow

Ship PharmaBot as a team: write the OpenAPI contract before writing code, keep a structured PR workflow, and use contract-driven development to eliminate integration surprises.

Asma Hafeez KhanMay 15, 20265 min read
API DesignOpenAPIGitHubTeam CollaborationContract-Driven DevelopmentPR Workflow
Share:š•

Why API-First Matters on a Team

When a frontend developer and a backend developer work simultaneously, they need a contract. Without one, the backend ships session_id and the frontend expects sessionId. You discover this at integration time — usually at 11pm before a demo.

API-first means you write the OpenAPI specification before writing a single line of implementation. Both sides code against the contract. Integration becomes a formality.


The PharmaBot OpenAPI Contract

FastAPI generates OpenAPI automatically from your Pydantic models and route decorators. Lock this contract in early:

Python
# pharmabot/main.py
from fastapi import FastAPI

app = FastAPI(
    title="PharmaBot API",
    description="AI-powered pharmaceutical information assistant",
    version="1.0.0",
    contact={"name": "PharmaBot Team", "email": "team@pharmabot.dev"},
    license_info={"name": "MIT"},
)

And annotate every route with response models:

Python
# pharmabot/api/chat.py
from pharmabot.schemas.chat import ChatRequest, StreamChunk

@router.post(
    "/chat",
    summary="Send a chat message",
    description="Send a message and receive a streamed SSE response.",
    response_description="Server-Sent Events stream of text tokens",
    responses={
        200: {"description": "Stream of SSE tokens"},
        400: {"description": "Invalid input or injection detected"},
        429: {"description": "Rate limit exceeded"},
        503: {"description": "LLM service temporarily unavailable"},
    },
    tags=["Chat"],
)
async def chat(request: ChatRequest, _=Depends(check_rate_limit)):
    ...

Visit http://localhost:8000/docs — your team has an interactive API explorer.


Exporting and Committing the Contract

Bash
# Export the OpenAPI schema as a file — commit it to the repo
curl http://localhost:8000/openapi.json > openapi.json
git add openapi.json
git commit -m "docs: export OpenAPI schema v1.0"

Now the contract is version-controlled. When you change an endpoint, the diff is visible in the PR:

DIFF
- "required": ["message", "session_id"]
+ "required": ["message", "session_id", "user_locale"]

A reviewer can immediately see the breaking change. A frontend developer can update their client before the backend ships.


Frontend Type Generation from the Schema

Instead of maintaining frontend types by hand, generate them from the OpenAPI schema:

Bash
# Install openapi-typescript
npm install -D openapi-typescript

# Generate types from the running API
npx openapi-typescript http://localhost:8000/openapi.json -o src/types/pharmabot-api.ts

The generated file:

TYPESCRIPT
// src/types/pharmabot-api.ts  (auto-generated — do not edit)
export interface paths {
  "/api/chat": {
    post: {
      requestBody: {
        content: {
          "application/json": components["schemas"]["ChatRequest"];
        };
      };
      responses: {
        200: { description: "Stream of SSE tokens" };
        400: { description: "Invalid input or injection detected" };
        429: { description: "Rate limit exceeded" };
      };
    };
  };
}

export interface components {
  schemas: {
    ChatRequest: {
      message: string;    // max 500 chars
      session_id: string; // UUID
    };
    HealthResponse: {
      status: "healthy" | "degraded";
      database: "ok" | "error";
      redis: "ok" | "error";
      azure_openai: "ok" | "error";
    };
  };
}

Every time the backend changes the contract, re-run the generator. TypeScript compilation catches mismatches before they reach production.


PR Workflow for the Team

Branch naming

feature/skill-06-ai-agents
fix/rate-limiter-edge-case
docs/update-openapi-schema
chore/bump-openai-sdk

PR checklist (add as PULL_REQUEST_TEMPLATE.md)

MARKDOWN
## What does this PR do?
<!-- One sentence -->

## How to test
<!-- Steps the reviewer should follow -->

## Checklist
- [ ] Tests pass locally (`pytest tests/ -v`)
- [ ] OpenAPI schema exported and committed if routes changed
- [ ] No secrets in code (use Key Vault references)
- [ ] Structured logging added for new operations
- [ ] CHANGELOG.md updated if this is a user-visible change

Review process

1. Author opens PR → CI runs automatically (tests + linting)
2. One reviewer approves → auto-merge enabled if CI passes
3. Merge to main → GitHub Actions deploys to staging
4. Staging smoke test passes → promote to production (manual approval gate)

Keeping the Team in Sync: CHANGELOG.md

MARKDOWN
# Changelog

## [Unreleased]
### Added
- Interaction Checker Agent with severity scoring (JSON output)
- Redis response caching for repeated queries

### Fixed
- Rate limiter now correctly handles clock skew on multi-replica deployments

## [1.0.0] — 2026-05-15
### Added
- Initial PharmaBot release
- Drug info and interaction check via Azure OpenAI
- Redis token bucket rate limiting
- Azure Container Apps deployment

Use Keep a Changelog format. Every PR that touches user-visible behavior gets a CHANGELOG entry.


Communication Protocol for Async Teams

| Situation | How to handle | |---|---| | Changing a request schema | Add a migration period — accept both old and new field names for 1 sprint | | Adding a new endpoint | Announce in team channel, update openapi.json, bump minor version | | Breaking a response field | Bump major version, communicate 2 sprints in advance | | Bug fix | Fix + test + PR — no announcement needed | | Performance regression | File issue tagged perf, add it to the next sprint board |


Checkpoint

Add the PR template and export the schema:

Bash
# Create PR template
mkdir -p .github
cat > .github/PULL_REQUEST_TEMPLATE.md << 'EOF'
## What does this PR do?

## How to test

## Checklist
- [ ] Tests pass (`pytest tests/ -v`)
- [ ] OpenAPI schema updated if routes changed
- [ ] No secrets in code
EOF

# Export current schema
curl http://localhost:8000/openapi.json | python3 -m json.tool > openapi.json

# Commit both
git add .github/PULL_REQUEST_TEMPLATE.md openapi.json
git commit -m "docs: add PR template and export OpenAPI schema"
git push origin main

Open a PR against your own repo — you should see the template auto-populate. From now on, every PR follows the same structure.

API Design Principles Knowledge Check

5 questions Ā· Test what you just learned Ā· Instant explanations

Enjoyed this article?

Explore the AI Systems learning path for more.

Found this helpful?

Share:š•

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.