blense.
HomeFrontendCodeAINewsGuidesCheatsheetsAbout

Technology decoded, always.

A curated read on AI, product, and the culture behind the code. No noise, no hype.

blense.

Technology with focus. Stories about what innovation really changes.

Sections

FrontendArtificial IntelligenceCode & DevTech News

Blense

AboutRSSPrivacy policyTermsCookies
© 2026 Blense · blense.fun
Code & Development

Framework vs SDK: Who Calls Whom

You don't call the framework — it calls you. The distinction that changes how you read documentation, with examples in JavaScript and Python.

Gby Genildo SouzaAug 238 min read
Framework vs SDK: Who Calls Whom

Every week someone asks me some version of this:

Is Express a framework or a library?

Is the OpenAI SDK a framework?

Should I learn frameworks or SDKs first?

It sounds like a beginner question, but the confusion sticks around well into year three of a career. And it is not for lack of studying — it is that almost nobody explains the mechanism behind either one. People memorize names instead.

Once you understand the mechanism, something useful happens: you read docs faster, debug with less guesswork, and pick tools without depending on a Twitter thread. Let us get into it.

The starting point: who calls whom

Three flows side by side — your code calling a library, a framework calling your code, an SDK reaching the outside world.
One question separates all three: who is calling whom?

Hold on to this sentence, because it settles about 80% of the confusion:

“You call the library. The framework calls you. The SDK talks to the outside world.

It sounds reductive. It is not. It is literally about your program''s flow of control — who is holding the steering wheel during execution.

Library: you are in charge

Diagram of your code calling a library function and receiving the return value.
You import it, you call it, execution returns to you. No "action," no movement.

A library is a bag of ready-made functions. You import it, call it when you feel like it, and execution comes right back to you.

JAVASCRIPT
import { format } from 'date-fns';const today = format(new Date(), 'MM/dd/yyyy');console.log(today); // your code is still driving
PYTHON
from datetime import datetimetoday = datetime.now().strftime('%m/%d/%Y')print(today)  # your code is still driving

You are the director. The library is the actor who steps in when you yell action. If you never call it, it does nothing.

Framework: it is in charge (and that is the whole point)

Your code at the center, surrounded by a framework ring with arrows pointing inward.
The Hollywood Principle in code: "don't call us, we'll call you."

Here everything flips. With a framework, you do not call its code — it calls yours.

This has a name: Inversion of Control (IoC), also known as the Hollywood Principle: do not call us, we will call you.

JAVASCRIPT
import express from 'express';const app = express();// You are NOT executing anything here.// You are REGISTERING a function and saying:// "when a GET hits /users, call this"app.get('/users', (req, res) => {  res.json([{ id: 1, name: 'Ana' }]);});app.listen(3000); // from this line on, the framework is in control
PYTHON
from fastapi import FastAPIapp = FastAPI()# Same idea: the decorator registers your function with the framework.# It decides when the function runs, not you.@app.get("/users")def list_users():    return [{"id": 1, "name": "Ana"}]

Notice what you did not write in either snippet:

  • you did not open a TCP socket

  • you did not parse raw HTTP text

  • you did not build the response headers

  • you did not write the infinite loop waiting for connections

  • you did not handle concurrent requests

All of that exists and is running right now. It just lives inside the framework.

What a framework really does under the hood

Circular three-stage diagram: register, bootstrap and execution loop.
Register, bootstrap, execution loop — the three-beat skeleton every framework runs on.

Every framework — web, UI, testing, whatever — runs on the same three-beat skeleton.

1. Registration phase. You declare your pieces: routes, components, middleware, jobs, test cases. The framework stores them in an internal structure. app.get(...) does not execute your function; it pushes a record onto a list.

2. Bootstrap phase. You hand over control (app.listen(), uvicorn main:app, npm test). The framework assembles what it needs: server, dependency injection, connections, component tree.

3. Execution loop. The framework runs forever, listening for events. When one arrives, it consults that list from step 1, finds your function, and calls it — handing you pre-chewed data (req, res, props, event).

Here is crude pseudocode of what Express is doing while you sleep:

JAVASCRIPT
// this is the framework, not your codewhile (true) {  const rawRequest = await socket.acceptConnection();  const req = parseHttp(rawRequest);            // becomes an object  const route = routingTable.find(req.method, req.url);  if (!route) return respond404();  for (const middleware of middlewares) {       // lifecycle    await middleware(req, res);  }  await route.handler(req, res);                // <- YOUR code, at last}

This is why frameworks have a lifecycle (useEffect, beforeEach, middleware, onMount). Those are hooks the framework offers so you can inject code at specific moments of the loop it controls. You do not get to choose when your code runs — only where it hangs.

It is also why frameworks are opinionated. By letting one drive, you accept its folder structure, its naming, its way of doing things. In exchange you get speed, and you do not rewrite HTTP parsing for the thousandth time in the history of computing.

SDK: the translator between your code and someone else''s system

 Your code connecting through an SDK block to a third-party service cloud.
A translator between your application and a distant system, so you never speak raw HTTP.

SDK stands for Software Development Kit. In practice, today, nearly every SDK is the same thing: a package that wraps a service''s API so you do not have to speak raw HTTP.

Without an SDK, integrating with a service looks like this:

PYTHON
import uuidimport requestsresponse = requests.post(    "https://api.example.com/v1/charges",    headers={        "Authorization": f"Bearer {api_key}",        "Content-Type": "application/json",        "Idempotency-Key": str(uuid.uuid4()),    },    json={"amount": 5000, "currency": "usd"},    timeout=30,)if response.status_code == 429:    ...  # now what? backoff? how many retries?if response.status_code >= 500:    ...  # retry? did the charge already go through?data = response.json()  # untyped dict, no autocomplete

With an SDK:

PYTHON
from stripe import StripeClientclient = StripeClient(api_key=api_key)charge = client.payment_intents.create({    "amount": 5000,    "currency": "usd",})print(charge.id)
JAVASCRIPT
import Stripe from 'stripe';const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);const charge = await stripe.paymentIntents.create({  amount: 5000,  currency: 'usd',});console.log(charge.id);

Three header lines and all the error handling vanished. But they did not stop existing — they just moved.

What a serious SDK handles for you

Six icons with labels for the work an SDK handles on your behalf.
Auth, retries, idempotency, pagination, typed errors. Boring code someone already battle-tested.
  • Authentication. Builds the header, refreshes expired tokens, reads the environment variable.

  • Serialization. Turns objects into JSON and JSON back into typed objects. You get autocomplete, which is underrated until you lose it.

  • Retry with exponential backoff. Got a 429 or a 503? Once you enable it, the client retries quickly on the first failure and then on an exponential backoff schedule, instead of giving up. Note it is opt-in: Stripe ships the mechanism, you configure it.

  • Idempotency. Generates a unique key so a retry does not charge your customer twice — Stripe stores that key for 24 hours and replays the original result. This one item alone pays for the SDK.

  • Pagination. Turns cursor juggling into a plain iterator.

  • Typed errors. RateLimitError, AuthenticationError — instead of comparing loose status numbers.

  • Versioning. Pins the remote API version so the server does not shift under your feet.

None of this is magic. It is boring, tedious, edge-case-riddled code that somebody already wrote, battle-tested in production, and maintains for you. That is the SDK.

The table worth taping to your monitor

Table comparing library, framework and SDK across seven traits.
The row that matters most is the highlighted one: cost to switch.

Trait

Library

Framework

SDK

Who calls whom

you call it

it calls you

you call it

Flow of control

yours

its

yours

Problem it solves

one specific task

your app structure

talking to an external system

Where the work runs

your machine

your machine

someone else''s server

How many per project

dozens

1 (rarely 2)

one per service

Cost of switching

low

brutal (rewrite)

medium

Examples

lodash, date-fns, pandas

React, Django, Rails, Spring

Stripe, AWS boto3, OpenAI

The most practical row is the cost of switching. Swapping date-fns for day.js is an afternoon. Swapping Django for FastAPI is a quarter. That is why framework choices deserve a meeting and library choices do not.

In real life, all three live in the same file

Code snippet with three annotations pointing at the framework, the SDK and the library.
Four lines, three roles. The framework is the stage, the SDK is the phone, the library is the workbench tool.

And there is nothing wrong with that:

PYTHON
import osfrom fastapi import FastAPI          # framework: runs the applicationfrom stripe import StripeClient      # SDK: talks to Stripefrom datetime import datetime        # library: one specific taskapp = FastAPI()stripe = StripeClient(api_key=os.environ["STRIPE_KEY"])@app.post("/subscribe")                       # the FRAMEWORK will call thisdef subscribe(amount: int):    charge = stripe.payment_intents.create({  # YOU call the SDK        "amount": amount,        "currency": "usd",    })    return {        "id": charge.id,        "created_at": datetime.now().isoformat(),  # YOU call the library    }

Three import lines, three different roles. The framework is the stage. The SDK is the phone. The library is the tool on the workbench.

What changes in your head after this

Two diagnostics side by side — a stack trace under a magnifier, and a network timeline under another.
Framework error? Look inward. SDK error? Look outward.

You debug better. A bug in framework code is almost never the framework''s fault — it is you using a hook at the wrong moment of the lifecycle. A bug in SDK code is almost always network, credentials, or rate limits. Knowing that cuts your investigation time in half.

You read docs in the right order. Framework docs: start with the lifecycle and the folder structure. SDK docs: start with auth and the error codes. Library docs: go straight to the function signature.

You choose with actual criteria. A framework is a marriage — check the community, the maintenance, the release cadence. An SDK is a vendor — prefer the official one, verify it has retries built in, and see how long since the last commit.

The rule of thumb

Decision tree leading from "does my code call this?" to framework, library or SDK.
Facing any unfamiliar package, just walk this path.

Facing any unfamiliar package, ask one question:

“Does my code call this, or will this call my code?

The three answers:
  • 1You call it and the work happens on your machine — library.
  • 2You call it and the work happens on another company''s server — SDK.
  • 3It calls you and dictates your project structure — framework.

That is it. Nothing else to memorize.


If this was useful, the natural next step is to open the source of whatever framework you use most and go find the main loop. Seeing with your own eyes the exact moment it calls your function is one of those things you cannot unsee — and it is what separates people who use tools from people who understand them.


Sources

  • Inversion of Control — Martin Fowler — the canonical definition. Fowler puts it plainly: the control is inverted, it calls me rather than me calling the framework. Also where the Hollywood Principle is tied to the concept.

  • Idempotent requests — Stripe API Reference — official documentation on idempotency keys: V4 UUIDs recommended, up to 255 characters, pruned after 24 hours.

  • Advanced error handling — Stripe Documentation — how the official libraries retry: first attempt quickly, then exponential backoff, respecting the Stripe-Should-Retry header. Confirms retries are opt-in and must be configured.

The elite dev's arsenal.
Astro: Island Architecture, Performance, and Where the Framework Truly Fits38 minMicrofrontends with Angular and Nx: what nobody tells you15 minTanStack Query: How to Eliminate Manual Server State Management in React6 minStop Using useMemo Wrong: A Practical Guide to React Performance14 min

The framework definition Fowler cites comes from Ralph Johnson and Brian Foote: the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user''s application code. That sentence is from 1988 — the idea in this article is older than most of the tools it uses as examples.

Keep exploring
Want more content like this?

Check out other articles in the same vein and keep the momentum.

See more articles
#JavaScript#Python#Runtime#Sdk

The elite dev's arsenal.

Microfrontends with Angular and Nx: what nobody tells you
Code & Development

Microfrontends with Angular and Nx: what nobody tells you

Every tutorial teaches how to set up microfrontends in minutes, but few reveal the architectural chaos that emerges after months of production deploys.

Genildo Souza · Aug 3 · 15 min
Astro: Island Architecture, Performance, and Where the Framework Truly Fits
Code & Development

Astro: Island Architecture, Performance, and Where the Framework Truly Fits

Astro challenges the modern web status quo by prioritizing static HTML and isolating interactivity into independent, lightweight code islands.

Genildo Souza · Aug 3 · 38 min
JavaScript Event Loop: The Heart of Non-Blocking Concurrency — Revisited in 2025
Code & Development

JavaScript Event Loop: The Heart of Non-Blocking Concurrency — Revisited in 2025

The Event Loop is not just a simple loop, but a sophisticated coordination mechanism between Call Stack, queues, and runtime. Essential for JS concurrency, it enables thousands of asynchronous operations without blocking the UI. This guide explores its structure, differences between Node.js and browsers, common bugs, advanced techniques, and 2025 trends like Structured Concurrency and scheduler.yield().

Genildo Souza · Jul 26 · 6 min
In this article
  • The starting point: who calls whom
  • Library: you are in charge
  • Framework: it is in charge (and that is the whole point)
  • What a framework really does under the hood
  • SDK: the translator between your code and someone else''s system
  • What a serious SDK handles for you
  • The table worth taping to your monitor
  • In real life, all three live in the same file
  • What changes in your head after this
  • The rule of thumb
  • Sources