Scenario 04: Unified Library API

Target Audience: Library authors, open source maintainers Difficulty: Intermediate Keywords: library design, API design, async library, dual interface


📋 The Problem

Library authors face a difficult choice:

Option 1: Sync-only API

  • Easy to use for beginners

  • Works in any context

  • But blocks event loop in async apps

  • Misses async performance benefits

Option 2: Async-only API

  • Non-blocking, efficient

  • Great for async apps

  • But requires asyncio.run() boilerplate for CLI/sync usage

  • Excludes sync-only users

Option 3: Dual APIs (both sync and async)

  • Maintain two implementations

  • Double the testing surface

  • Risk of behavior divergence

  • Confusing for users (which one to use?)

Common examples:

  • HTTP clients (requests vs httpx/aiohttp)

  • Database drivers (psycopg2 vs asyncpg)

  • Cloud SDK clients


💡 Solution with SmartAsync

Single implementation, dual interface:

  • Write one async implementation

  • Users can call it sync or async

  • No duplicate code

  • Consistent behavior

  • Automatic adaptation to context

Pattern:

Library with @smartasync methods
├─→ CLI users: call without await
├─→ Script users: call without await
├─→ Async app users: call with await
└─→ Async lib users: call with await

🎯 When to Use

Ideal for:

  • New libraries targeting broad audience

  • HTTP/API clients

  • Data processing libraries

  • Developer tools and utilities

  • Configuration management libraries

Perfect when:

  • You want maximum user adoption

  • Users span sync and async codebases

  • You don’t want to maintain two APIs

  • Behavior should be identical in both modes


⚠️ Considerations

Design Decisions

Performance expectations:

  • Sync calls: ~100μs overhead from asyncio.run()

  • Usually negligible compared to I/O operations

  • Document this for performance-critical users

API documentation:

  • Show both usage patterns in docs

  • Make it clear the same method works both ways

  • Provide migration guides from sync-only libs

Semantic versioning:

  • Adding SmartAsync to existing async-only API: minor version bump

  • Users can adopt gradually (no breaking change)

When NOT to Use

Avoid if:

  • Library is performance-critical (nanosecond-level)

  • You need different sync/async behavior

  • Target audience is async-only (no benefit)

  • You want explicit control over thread pools

Alternatives:

  • Separate sync/async packages (like requests/httpx)

  • Async-only with examples of asyncio.run() usage

  • Wrapper package pattern (thin sync wrapper over async core)



📚 Real-World Examples

Libraries that could benefit:

  • httpx - Already async-first, could add sync mode

  • boto3 - AWS SDK (currently sync-only)

  • stripe-python - Payment API (currently sync-only)

  • openai-python - AI API client


🎯 Success Metrics

Your unified library API is successful when:

  • Users don’t need to know about async/sync distinction

  • No “sync” vs “async” in method names

  • Single import works for everyone

  • Issue tracker shows diverse usage patterns

  • Both sync and async users are happy


Next Steps: