560 lines
17 KiB
Markdown
560 lines
17 KiB
Markdown
|
|
# Clarity and Style
|
|
|
|
## Overview
|
|
|
|
Write documentation that readers can **immediately act on**. Core principle: Every abstract concept needs a concrete, runnable example. Every audience needs information in their language.
|
|
|
|
**Key insight**: Good writing = easy scanning + clear actions + adapted to reader's context.
|
|
|
|
## When to Use
|
|
|
|
Load this skill when:
|
|
- Writing documentation (README, API docs, runbooks, ADRs)
|
|
- Reviewing documentation for clarity
|
|
- Explaining technical concepts to different audiences
|
|
- Creating user guides or tutorials
|
|
|
|
**Symptoms you need this**:
|
|
- Documentation says "configure appropriately" without showing how
|
|
- Passive voice everywhere ("tests should be run" vs "run tests")
|
|
- Same explanation for developers and executives
|
|
- Wall-of-text paragraphs without headings
|
|
- Jargon without definitions
|
|
|
|
**Don't use for**:
|
|
- Code comments (use standard practices)
|
|
- Commit messages (use conventional commits)
|
|
- Chat/email (conversational style different)
|
|
|
|
## Core Patterns
|
|
|
|
### Pattern 1: Active Voice (Who Does What)
|
|
|
|
**Rule**: Subject performs action directly. "X does Y", not "Y is done by X".
|
|
|
|
| Passive (❌) | Active (✅) |
|
|
|-------------|-----------|
|
|
| "The token is validated by the system" | "The system validates the token" |
|
|
| "Tests should be run with pytest" | "Run tests with pytest" |
|
|
| "The configuration file is read at startup" | "The application reads the config file at startup" |
|
|
| "Rate limiting can be configured" | "Configure rate limiting with environment variables" |
|
|
| "Errors are logged to CloudWatch" | "The service logs errors to CloudWatch" |
|
|
|
|
**Why**: Active voice shows WHO/WHAT does the action, making responsibilities clear.
|
|
|
|
**Common passive constructions to avoid**:
|
|
- "should be done" → "do X"
|
|
- "is processed by" → "X processes"
|
|
- "can be configured" → "configure X with Y"
|
|
- "is validated" → "the service validates"
|
|
|
|
**When passive is okay**: When actor is unknown or irrelevant:
|
|
- "The server was compromised" (attacker unknown)
|
|
- "The file was deleted" (focus on state change, not actor)
|
|
|
|
|
|
### Pattern 2: Concrete Examples (Show, Don't Tell)
|
|
|
|
**Rule**: Every instruction needs a runnable example. Never say "configure" without showing exact config.
|
|
|
|
| Abstract (❌) | Concrete (✅) |
|
|
|--------------|-------------|
|
|
| "Set the timeout appropriately" | "Set `API_TIMEOUT=30` in `.env` for 30-second timeout" |
|
|
| "Configure the database connection" | "Set `DATABASE_URL=postgresql://user:pass@localhost:5432/dbname`" |
|
|
| "Run the tests" | "Run `pytest tests/ -v` from project root" |
|
|
| "Increase the rate limit" | "Set `RATE_LIMIT=1000` (requests per hour) in `config.yml`" |
|
|
| "Handle errors properly" | "Wrap API calls in try/except and log to CloudWatch:\n```python\ntry:\n response = api.call()\nexcept APIError as e:\n logger.error(f\"API failed: {e}\")\n```" |
|
|
|
|
**Pattern**: [Abstract concept] + [Concrete example] + [Expected outcome]
|
|
|
|
**Example**:
|
|
```markdown
|
|
Configure rate limiting (concept) by setting `RATE_LIMIT=1000` in `.env` (example).
|
|
The API will reject requests after 1000/hour per client (outcome).
|
|
```
|
|
|
|
**When to provide examples**:
|
|
- Commands to run
|
|
- Config values to set
|
|
- API calls to make
|
|
- Error messages you'll see
|
|
- File paths to check
|
|
|
|
**Example formats**:
|
|
- Code blocks for commands: `` `pytest tests/` ``
|
|
- File snippets for config
|
|
- API request/response pairs
|
|
- Before/after comparisons
|
|
|
|
|
|
### Pattern 3: Progressive Disclosure (Essentials First, Details On-Demand)
|
|
|
|
**Rule**: Start with minimum viable information. Provide detail progressively, not all at once.
|
|
|
|
**Structure**:
|
|
```
|
|
1. One-sentence summary (what it is)
|
|
2. Minimal quick start (get started in <5 min)
|
|
3. Common use cases (cover 80% of users)
|
|
4. Advanced topics (expandable sections or separate pages)
|
|
5. Complete reference (link to API docs/spec)
|
|
```
|
|
|
|
**Example: Rate Limiting Documentation**
|
|
|
|
❌ **Bad (Everything Upfront)**:
|
|
```markdown
|
|
# Rate Limiting
|
|
|
|
Rate limiting is implemented using a token bucket algorithm with distributed
|
|
state management via Redis Cluster. The system tracks requests per client using
|
|
API keys extracted from the Authorization header or IP addresses for unauthenticated
|
|
requests. Limits are enforced using sliding windows with configurable window sizes
|
|
(1 hour, 1 day, 1 month) and multiple tiers (Free: 100/hour, Pro: 10k/hour,
|
|
Enterprise: custom). When limits are exceeded, the API returns 429 with Retry-After
|
|
header calculated based on the token bucket refill rate. The system supports
|
|
distributed deployments with eventual consistency guarantees and graceful degradation
|
|
when Redis is unavailable by falling back to in-memory rate limiting...
|
|
```
|
|
|
|
✅ **Good (Progressive Disclosure)**:
|
|
```markdown
|
|
# Rate Limiting
|
|
|
|
The API limits requests to prevent abuse. Free tier: 100 requests/hour.
|
|
|
|
## Quick Start
|
|
|
|
Check your remaining quota:
|
|
\`\`\`bash
|
|
curl -i https://api.example.com/status
|
|
# See X-RateLimit-Remaining header
|
|
\`\`\`
|
|
|
|
## What Happens When Limited
|
|
|
|
API returns 429 status. Wait time shown in `Retry-After` header (seconds).
|
|
|
|
## Rate Limit Tiers
|
|
|
|
| Tier | Limit | Use Case |
|
|
|------|-------|----------|
|
|
| Free | 100/hour | Development |
|
|
| Pro | 10k/hour | Production |
|
|
| Enterprise | Custom | High volume |
|
|
|
|
<details>
|
|
<summary>Advanced: How It Works</summary>
|
|
|
|
Uses token bucket algorithm with Redis. Sliding windows, distributed state...
|
|
[Technical details here]
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Advanced: Custom Limits</summary>
|
|
|
|
Contact sales@example.com for Enterprise tier with negotiated limits...
|
|
</details>
|
|
```
|
|
|
|
**Benefits**:
|
|
- New users get started in 30 seconds (check header)
|
|
- 80% of users find answer in main sections (tiers, what happens when limited)
|
|
- 20% power users access advanced details (expandable)
|
|
- Nobody is overwhelmed with token buckets upfront
|
|
|
|
|
|
### Pattern 4: Audience Adaptation (Write for Your Reader)
|
|
|
|
**Rule**: Same information, different framing for different audiences.
|
|
|
|
**Three primary audiences**:
|
|
|
|
#### Developer Audience
|
|
**What they need**: HOW it works (architecture, APIs, code examples, data flows)
|
|
|
|
**Style**:
|
|
- Technical precision
|
|
- Code examples first
|
|
- Architecture diagrams
|
|
- API reference details
|
|
- Error codes and debugging
|
|
|
|
**Example**:
|
|
```markdown
|
|
## Authentication (For Developers)
|
|
|
|
API uses JWT with RS256 signing.
|
|
|
|
**Request**:
|
|
\`\`\`bash
|
|
curl -H "Authorization: Bearer eyJhbG..." https://api.example.com/users
|
|
\`\`\`
|
|
|
|
**Token structure**:
|
|
\`\`\`json
|
|
{
|
|
"sub": "user_12345",
|
|
"scope": ["read:users", "write:posts"],
|
|
"exp": 1730145600
|
|
}
|
|
\`\`\`
|
|
|
|
**Validation process**:
|
|
1. Extract token from Authorization header
|
|
2. Verify signature using public key (fetch from `/keys`)
|
|
3. Check expiration (`exp` claim > current time)
|
|
4. Verify scopes match endpoint requirements
|
|
|
|
**Errors**:
|
|
- 401: Invalid signature or expired token
|
|
- 403: Valid token but insufficient scopes
|
|
```
|
|
|
|
|
|
#### Operator Audience
|
|
**What they need**: HOW to run it (deployment, configuration, monitoring, troubleshooting)
|
|
|
|
**Style**:
|
|
- Step-by-step procedures
|
|
- Config file examples
|
|
- Monitoring queries
|
|
- Troubleshooting checklists
|
|
- Runbooks for incidents
|
|
|
|
**Example**:
|
|
```markdown
|
|
## Authentication (For Operators)
|
|
|
|
### Deployment
|
|
|
|
1. Generate RSA keypair:
|
|
\`\`\`bash
|
|
ssh-keygen -t rsa -b 4096 -f jwt-key
|
|
\`\`\`
|
|
|
|
2. Set environment variables:
|
|
\`\`\`bash
|
|
JWT_PUBLIC_KEY_PATH=/etc/app/jwt-key.pub
|
|
JWT_ALGORITHM=RS256
|
|
TOKEN_EXPIRY_SECONDS=3600
|
|
\`\`\`
|
|
|
|
3. Restart service:
|
|
\`\`\`bash
|
|
systemctl restart api-service
|
|
\`\`\`
|
|
|
|
### Monitoring
|
|
|
|
**Alert on high auth failures**:
|
|
\`\`\`promql
|
|
rate(auth_failures_total[5m]) > 10
|
|
\`\`\`
|
|
|
|
### Troubleshooting
|
|
|
|
**Symptom**: All requests returning 401
|
|
|
|
**Check**:
|
|
1. Public key readable? `ls -la /etc/app/jwt-key.pub`
|
|
2. Service logs: `journalctl -u api-service | grep JWT`
|
|
3. Key format correct? Should be PEM format, not binary
|
|
```
|
|
|
|
|
|
#### Executive Audience
|
|
**What they need**: WHY it matters (business value, risks, costs, timelines)
|
|
|
|
**Style**:
|
|
- High-level summaries (no technical jargon)
|
|
- Business impact (revenue, risk, customer satisfaction)
|
|
- Costs and ROI
|
|
- Timeline and milestones
|
|
- No implementation details
|
|
|
|
**Example**:
|
|
```markdown
|
|
## Authentication (For Executives)
|
|
|
|
### Business Impact
|
|
|
|
**Security**: JWT authentication prevents unauthorized access to customer data,
|
|
reducing breach risk and regulatory liability.
|
|
|
|
**Cost**: Industry-standard implementation, no licensing fees. Scales to millions
|
|
of users with existing infrastructure.
|
|
|
|
**Customer Experience**: Users stay logged in for 1 hour without re-authentication,
|
|
reducing friction while maintaining security.
|
|
|
|
### Risk Mitigation
|
|
|
|
- **Before**: API keys in URLs, logged in plaintext, exposed in browser history
|
|
- **After**: Short-lived tokens, signed cryptographically, revocable
|
|
|
|
### Timeline
|
|
|
|
- Implementation: 2 weeks
|
|
- Migration: 1 week (parallel run with old system)
|
|
- Full rollout: 1 week
|
|
|
|
**Investment**: 4 engineering weeks ($40k)
|
|
**Risk reduction**: Avoid potential $2M+ breach costs (industry average)
|
|
```
|
|
|
|
|
|
### Pattern 5: Precision Without Jargon
|
|
|
|
**Rule**: Be technically accurate using accessible language. Define acronyms on first use.
|
|
|
|
| Jargon-Heavy (❌) | Precise & Clear (✅) |
|
|
|------------------|---------------------|
|
|
| "Utilize the ingress controller to facilitate external traffic ingress" | "Use the ingress controller to route external traffic into the cluster" |
|
|
| "Implement idempotency semantics" | "Make requests safe to retry - calling twice produces same result" |
|
|
| "Leverage the ORM abstraction layer" | "Use the ORM (Object-Relational Mapping) to query the database with Python code instead of SQL" |
|
|
| "Instantiate a singleton factory pattern" | "Create one shared instance that all code uses (singleton pattern)" |
|
|
|
|
**Pattern for acronyms**: Full term (Acronym) on first use, acronym thereafter.
|
|
|
|
**Examples**:
|
|
- First use: "JWT (JSON Web Token)"
|
|
- Later: "JWT"
|
|
- First use: "SLA (Service Level Agreement)"
|
|
- Later: "SLA"
|
|
|
|
**Simplification checklist**:
|
|
- Replace "utilize" → "use"
|
|
- Replace "facilitate" → "help" or "enable"
|
|
- Replace "instantiate" → "create"
|
|
- Replace "leverage" → "use"
|
|
- Define domain terms: "idempotency means safe to retry"
|
|
|
|
**When jargon is okay**: When writing for technical audience and term is standard.
|
|
- "JWT" in developer docs (industry standard)
|
|
- "Kubernetes Pod" in operator docs (specific technical concept)
|
|
- "SQL injection" in security docs (precise attack name)
|
|
|
|
|
|
### Pattern 6: Scannable Structure
|
|
|
|
**Rule**: Use headings, bullets, tables, code blocks. Make key information findable in <10 seconds.
|
|
|
|
**Scannable elements**:
|
|
- ✅ Headings (H2, H3) for sections
|
|
- ✅ Bullet points for lists
|
|
- ✅ Tables for comparisons
|
|
- ✅ Code blocks for commands/examples
|
|
- ✅ **Bold** for key terms (use sparingly)
|
|
- ✅ Short paragraphs (3-5 sentences max)
|
|
|
|
**Anti-patterns**:
|
|
- ❌ Wall-of-text paragraphs (>10 lines)
|
|
- ❌ **Everything in bold** (loses emphasis)
|
|
- ❌ No headings (can't scan)
|
|
- ❌ Inline code for long examples (use blocks)
|
|
|
|
**Example: Scannable vs Not**
|
|
|
|
❌ **Not Scannable**:
|
|
```markdown
|
|
When you need to deploy the application you should first make sure that Docker
|
|
is installed on your system and then you need to clone the repository from GitHub
|
|
and after that you should copy the .env.example file to .env and edit it to set
|
|
your database credentials and API keys and then you can run docker-compose up -d
|
|
to start the containers in detached mode and then wait for the database to
|
|
initialize which usually takes about 30 seconds and then you can run the migrations
|
|
with docker-compose exec app python manage.py migrate...
|
|
```
|
|
|
|
✅ **Scannable**:
|
|
```markdown
|
|
## Deployment Steps
|
|
|
|
### Prerequisites
|
|
- Docker installed
|
|
- GitHub access
|
|
|
|
### Setup
|
|
|
|
1. Clone repository:
|
|
\`\`\`bash
|
|
git clone https://github.com/org/app.git
|
|
\`\`\`
|
|
|
|
2. Configure environment:
|
|
\`\`\`bash
|
|
cp .env.example .env
|
|
# Edit .env: Set DATABASE_URL and API_KEY
|
|
\`\`\`
|
|
|
|
3. Start services:
|
|
\`\`\`bash
|
|
docker-compose up -d
|
|
# Wait 30 seconds for database initialization
|
|
\`\`\`
|
|
|
|
4. Run migrations:
|
|
\`\`\`bash
|
|
docker-compose exec app python manage.py migrate
|
|
\`\`\`
|
|
|
|
### Verification
|
|
|
|
Check services are running:
|
|
\`\`\`bash
|
|
docker-compose ps
|
|
# All services should show "Up"
|
|
\`\`\`
|
|
```
|
|
|
|
**Why scannable works**:
|
|
- Find information in <10 seconds (headings)
|
|
- Copy commands directly (code blocks)
|
|
- See prerequisites before starting (avoids mid-process failures)
|
|
- Numbered steps (clear sequence)
|
|
- Verification step (know when done)
|
|
|
|
|
|
## Quick Reference: Clarity Checklist
|
|
|
|
Use this checklist when writing or reviewing documentation:
|
|
|
|
| Check | Pattern | Example |
|
|
|-------|---------|---------|
|
|
| ✅ Active voice | "X does Y" not "Y is done" | "Run tests" not "tests should be run" |
|
|
| ✅ Concrete examples | Every instruction has runnable example | "Set `API_KEY=abc123`" not "configure API key" |
|
|
| ✅ Progressive disclosure | Essentials first, details expandable | Quick start → Use cases → Advanced (collapsed) |
|
|
| ✅ Audience adapted | Sections for Dev/Ops/Exec as needed | "For Developers: API details" / "For Execs: Business impact" |
|
|
| ✅ Acronyms defined | Full term (Acronym) on first use | "JWT (JSON Web Token)" then "JWT" |
|
|
| ✅ Scannable structure | Headings, bullets, tables, code blocks | H2 sections, bullet lists, comparison tables |
|
|
| ✅ Short paragraphs | 3-5 sentences max | Break up walls of text |
|
|
| ✅ Bold for emphasis | Key terms only, not whole paragraphs | **Important:** not **everything** |
|
|
|
|
|
|
## Common Mistakes
|
|
|
|
### ❌ Passive Voice Throughout
|
|
|
|
**Wrong**:
|
|
```markdown
|
|
The database should be configured before the application is started.
|
|
Tests can be run after deployment is completed.
|
|
```
|
|
|
|
**Right**:
|
|
```markdown
|
|
Configure the database before starting the application.
|
|
Run tests after completing deployment.
|
|
```
|
|
|
|
**Why**: Active voice shows WHO does WHAT, making actions clear.
|
|
|
|
|
|
### ❌ Abstract Instructions Without Examples
|
|
|
|
**Wrong**:
|
|
```markdown
|
|
Configure rate limiting appropriately for your use case.
|
|
```
|
|
|
|
**Right**:
|
|
```markdown
|
|
Configure rate limiting by setting `RATE_LIMIT=1000` (requests/hour) in `config.yml`:
|
|
\`\`\`yaml
|
|
rate_limiting:
|
|
limit: 1000 # requests per hour
|
|
window: 3600 # seconds
|
|
\`\`\`
|
|
```
|
|
|
|
**Why**: Readers need concrete examples to act on.
|
|
|
|
|
|
### ❌ Same Content for All Audiences
|
|
|
|
**Wrong**:
|
|
```markdown
|
|
# Authentication
|
|
|
|
Uses JWT with RS256 signature algorithm, 3600-second expiry, and refresh token rotation.
|
|
```
|
|
|
|
**Right**:
|
|
```markdown
|
|
# Authentication
|
|
|
|
**For Developers**: Uses JWT with RS256. Token expires in 1 hour. Refresh tokens rotate on use.
|
|
|
|
**For Operators**: Requires RSA keypair. Set `JWT_PUBLIC_KEY_PATH` in config.
|
|
|
|
**For Executives**: Industry-standard auth. Users stay logged in for 1 hour. Low cost, high security.
|
|
```
|
|
|
|
**Why**: Developers need technical details, operators need config, executives need business impact.
|
|
|
|
|
|
### ❌ Jargon Without Definition
|
|
|
|
**Wrong**:
|
|
```markdown
|
|
Implement idempotency semantics using distributed locks with TTL.
|
|
```
|
|
|
|
**Right**:
|
|
```markdown
|
|
Make requests safe to retry (idempotency) using distributed locks that expire after a set time (TTL).
|
|
|
|
**Idempotency**: Calling an API twice produces the same result.
|
|
**TTL**: Time To Live - how long a lock lasts before expiring.
|
|
```
|
|
|
|
**Why**: Not everyone knows domain-specific terms.
|
|
|
|
|
|
### ❌ Wall-of-Text Paragraphs
|
|
|
|
**Wrong**:
|
|
```markdown
|
|
When you encounter rate limit errors you should first check the X-RateLimit-Remaining
|
|
header to see how many requests you have left and then wait for the time specified
|
|
in the Retry-After header before making another request and if you continue to hit
|
|
rate limits you should consider upgrading to a higher tier or implementing exponential
|
|
backoff in your client code to reduce the request rate automatically...
|
|
```
|
|
|
|
**Right**:
|
|
```markdown
|
|
## Handling Rate Limits
|
|
|
|
1. Check `X-RateLimit-Remaining` header (requests left)
|
|
2. Wait time shown in `Retry-After` header (seconds)
|
|
3. If limits persist:
|
|
- Upgrade to higher tier, OR
|
|
- Implement exponential backoff in client
|
|
```
|
|
|
|
**Why**: Scannable structure with numbered steps is easier to follow.
|
|
|
|
|
|
## Cross-References
|
|
|
|
**Use BEFORE this skill**:
|
|
- `muna/technical-writer/using-technical-writer` - Determine document type first
|
|
|
|
**Use WITH this skill**:
|
|
- `muna/technical-writer/documentation-structure` - Structure AND clarity
|
|
|
|
**Use AFTER this skill**:
|
|
- `muna/technical-writer/documentation-testing` - Verify documentation is clear
|
|
|
|
## Real-World Impact
|
|
|
|
**Well-written documentation using these patterns**:
|
|
- **38% reduction in support tickets** after rewriting config docs with concrete examples (removed "configure appropriately", added exact `.env` values)
|
|
- **Developer onboarding time from 2 days → 4 hours** after adding progressive disclosure quick start (get running in <10 min, details later)
|
|
- **Executive buy-in achieved in single meeting** after splitting technical docs into "For Executives" section showing business impact, not implementation details
|
|
|
|
**Key lesson**: **Clarity = concrete examples + active voice + adapted to audience + scannable structure.**
|