Library vs. SDK vs. Framework vs. API: Who Calls Whom?
A library provides reusable functionality that your code calls. An SDK provides development tools for working with a particular platform or service. A framework typically manages part of your application's execution flow and calls your code at predefined points. An API defines the interface through which software components interact. These are different roles, not four mutually exclusive categories. An SDK can contain libraries and wrappers around service APIs, while a framework also exposes APIs of its own.
Once you can read a small Python project, a different question starts to matter: Who controls execution? Who calls the next function? Where does the data go? And which layer is responsible when something fails?
The previous article, From a Python Script to a Reproducible Small Project, covered program entry points, imports, modules, and project structure. We will use those foundations here without repeating them. The goal is to understand the relationships between the tools you encounter, rather than memorize a list of product names.
Table of Contents
- Four Terms That Describe Different Roles
- The Same Task, with Control in Different Places
- A Runnable Exercise: Let the Runner Call Your Function
- Controlled Failures: Identify the Responsible Layer
- Four Questions to Ask When Reading Unfamiliar Code
- What You Should Be Able to Explain
- Technical References and Evidence Boundaries
Four Terms That Describe Different Roles
| Term | What it describes | Who usually decides when to use it? | Where to investigate first |
|---|---|---|---|
| Library | Reusable functionality, such as Python's statistics module |
Your application calls its functions | Inputs and the function contract |
| SDK (software development kit) | A toolset for developing against a platform or service; it may include libraries, documentation, and examples | Your code calls interfaces supplied by the kit; SDKs differ | SDK method, configuration, version, and underlying service |
| Framework | Application structure and part of its lifecycle, often invoking handlers you register | The framework typically schedules the registered code | Registration rules, lifecycle, and supplied data |
| API (application programming interface) | A contract defining operations and their inputs, outputs, and errors | Whichever component calls the interface | Names, parameters, return values, and error contract |
An API does not have to be a network endpoint. Calling statistics.mean([10, 20]) uses a Python programming interface. An SDK might expose interfaces for remote services, but the interface and its network transport are separate concerns. HTTP requests and JSON responses are reserved for Article 07.
The Same Task, with Control in Different Places
Suppose you need an average of synthetic study-minute records.
Approach A: Your application calls a library.
from statistics import mean
minutes = [10, 20, 30]
average = mean(minutes)
print(f"average={average:.1f}")
You launch Python, your code calls mean, the library returns 20, and your code prints the result. The library does not determine when the whole application starts or what runs next. The verified execution of this separate snippet on Windows / PowerShell / Python 3.14.6 printed average=20.0.
Approach B: Give a handler to a controller. A real framework is more than a library with a callback (a function you hand to another component to invoke at an agreed point): it typically manages startup, event dispatch, configuration, or other lifecycle responsibilities. To isolate the call direction without installing a framework, we use a minimal educational Runner simulator, not a production framework.
You start main()
-> educational Runner.run() controls the loop
-> Runner invokes the registered on_record(record)
-> on_record returns its result
-> Runner collects the result
The important change is who decides when the handler runs. This is an example of inversion of control (IoC). Real systems can combine both call directions, so inspect their entry point rather than infer behavior from the product label.
Where does an SDK fit? The official Boto3 client documentation describes application code constructing a client and invoking its service-operation methods: your application -> Boto3 client interface -> service operation, with results or errors returned to the caller. This is a documentation-based example only: we did not install Boto3, configure credentials, construct a client, or call AWS. An SDK is a collection of development tools; an API is the contract used through an interface. Do not assume every SDK automatically retries, authenticates, or validates requests.
A Runnable Exercise: Let the Runner Call Your Function
Create a folder named control-flow-lab and place this entire program in main.py. It uses synthetic input, Python 3 standard-library functionality, no network connection, and no third-party packages.
# main.py — educational framework simulator, not a production framework
from statistics import mean
class Runner:
def __init__(self):
self.handler = None
def register(self, handler):
if not callable(handler):
raise TypeError("handler must be callable")
self.handler = handler
def run(self, batches):
if self.handler is None:
raise RuntimeError("no handler registered")
output = []
for batch in batches:
print("Runner -> handler")
output.append(self.handler(batch))
return output
def on_record(values):
if not values:
raise ValueError("empty batch")
return round(mean(values), 1)
def main():
runner = Runner()
runner.register(on_record)
print(runner.run([[10, 20, 30], [40, 50]]))
if __name__ == "__main__":
main()
Run it from that folder:
python main.py
Observed in Codex's fresh six-case verification on 2026-10-09 (Windows / PowerShell / Python 3.14.6) (not a guarantee across all Python versions):
Runner -> handler
Runner -> handler
[20, 45]
Predict before editing: change the second batch to [40, 50, 60]. The second average should become 50, while Runner -> handler still prints twice. Codex's fresh verification also observed the resulting list [20, 50]. Changing the data does not necessarily change the number of handler calls.
Controlled Failures: Identify the Responsible Layer
Keep your original file and make one change at a time, restoring the file before the next experiment.
- Comment out
runner.register(on_record). ExpectRuntimeError: no handler registered. The Runner's registration contract was not satisfied; this is not a Python syntax error. - Restore registration, then change the second batch to
[]. Expect twoRunner -> handlerlines beforeValueError: empty batchfromon_record. Dispatch occurred; the handler rejected its input. - Restore the batch, then change the registration to
runner.register("on_record"). ExpectTypeError: handler must be callable. A string naming a function is not the function object expected by the registration API.
Codex independently reran six bounded cases on 2026-10-09 using Windows / PowerShell / Python 3.14.6: the standalone library example, normal Runner execution, the changed-input variant, and these three deliberate failures. The failure cases exited with nonzero status. These observations establish only those examples in that environment—not universal Python-version compatibility or any real framework's behavior. When your output differs, read the final exception type in the traceback, then trace which function raised it and who passed the data.
Four Questions to Ask When Reading Unfamiliar Code
When reading unfamiliar code or a tutorial, mark four boundaries:
- Who starts execution? Do you run
main.py, call a library function, or start an application through a framework? - Who calls whom? Does your code call the package, or does a framework invoke a registered callback through events, inheritance, or configuration?
- Where does the data travel? What arrives at the entry point, which interface receives it, and what comes back? Which details does the SDK or framework hide?
- Which layer failed? Is this syntax, registration, your callback's logic, or an external service? Use observed errors and documentation rather than blaming a tool category.
Choose the abstraction that solves the problem. For a single transformation, investigate a library first. For several capabilities tied to a particular service, assess its SDK. If you need an established lifecycle, routing, or event-dispatch structure, consider a framework. More abstraction is not automatically better: it can reduce repeated control-flow code while obscuring where failures arise.
What You Should Be Able to Explain
Without looking back at the example, draw two sequences: you -> mean -> return value and you start Runner -> Runner -> on_record -> Runner. Explain the contract boundary involved in each of the three failures and predict what changes when the input batches change. This is the minimum learning checkpoint; hands-on competence still requires running the example yourself and interpreting the results.
You can stop here and move to the next article. You do not need to install FastAPI, Django, or multiple SDKs, or refactor a project merely to distinguish these roles. Earlier articles cover the Python execution environment, data flow, and a reproducible small project. Git belongs to Articles 05–06, and the mechanics of HTTP/JSON service calls belong to Article 07.
Course basis: AI_Engineering_Foundations_v1.0, section S04 (2026-09-23; retained extract from page 12), and the approved AI Engineering Foundations series plan. The Runner is a synthetic teaching device, not a complete simulation of any particular framework.
Technical References and Evidence Boundaries
These sources were consulted for the approved Chinese version on 2026-10-08; documentation can change with product versions.
- Python
statisticsdocumentation:meanbehavior and its input/output contract.round(mean(values), 1)does not guarantee afloat; observed output is reported rather than an assumed type. - MDN's definition of API: software interfaces extend beyond web services.
- AWS introduction to SDKs and Boto3 client guide: SDK tools versus service-operation interfaces. Boto3 was studied from documentation, not executed.
- Martin Fowler on inversion of control and Flask lifecycle documentation: framework-managed execution and registered application logic. Flask itself was not installed or tested for this article.
Evidence boundary: Six specific Python scenarios were verified on 2026-10-09 under Windows / PowerShell / Python 3.14.6: the library example, normal Runner execution, a changed-input variant, and three intentional failures. These observations do not establish compatibility with every Python version or real-framework behavior. The Boto3 and Flask discussion is documentation-based; neither SDK/service calls nor Flask execution were tested.