Introduction: What Makes a Framework a Framework?

Web Framework Internals

Chapter 1 · Introduction: What Makes a Framework a Framework?

Django looks nothing like Express, which looks nothing like Rails, which looks nothing like Laravel. Different languages, different syntax, different philosophies loudly advertised on their own homepages. And yet underneath all of that, nearly every one of them is solving the same small handful of real problems — the same problems every single time, regardless of which language or company built the framework in question. This course looks at those problems directly, one at a time: what each one actually is, and then how genuinely differently (or similarly) real frameworks choose to solve it.

Library vs. Framework: A Real, Precise Distinction

"Framework" gets used loosely enough in casual conversation that it's worth pinning down exactly what separates it from a library — not as a vague feeling, but as a genuine, testable property: who calls whom.

A library is a collection of code your code calls, on your own schedule, in whatever order you decide. A framework inverts that relationship — it provides the outer structure and the actual control flow, and it calls your code, at moments and in an order the framework itself decides. This is genuinely old, well-established terminology: Martin Fowler's real 2004 article, "Inversion of Control Containers and the Dependency Injection Pattern," was written specifically to clarify a term that was already, even then, being used loosely and inconsistently across the industry. The informal shorthand many developers reach for — "don't call us, we'll call you," sometimes nicknamed the Hollywood Principle — captures the same real idea: the framework is in charge of the flow, not your own code.

PropertyLibraryFramework
Who calls whomYour code calls the libraryThe framework calls your code
Control flowOwned by your own applicationOwned by the framework itself
A concrete examplerequests.get(url) — you decide exactly when this runsA Django view function, only ever invoked by Django itself, only once a matching URL arrives
What you actually writeCalls into the library, wherever you choose to put themPieces the framework's own lifecycle expects to find and call — a route handler, a middleware function, a model class

This distinction genuinely matters for the rest of this course, because every one of the four capabilities below is, at its core, a specific place where a framework has decided to take control away from you and call your code itself — a route handler only runs when the framework's own router decides a URL matches it; a template only renders when the framework's own view-rendering machinery reaches that point in a request.

Four Real, Load-Bearing Capabilities

Real frameworks vary enormously in scope — some try to do almost everything, others deliberately do very little — but a genuinely useful, real-world starting definition keeps narrowing down to the same short list. This course picks four to go deep on, one chapter of "how it actually works" followed by one chapter of "how real frameworks differ" for each:

CapabilityThe real problem it solves
RoutingMatching an incoming URL (and HTTP method) to the specific piece of your own code that should handle it
TemplatingTurning data into an actual HTML response, without hand-concatenating strings and without a raw injection vulnerability
Data accessGetting data in and out of a real database without hand-writing every SQL statement for every table by hand
MiddlewareRunning shared logic — auth checks, logging, error handling — around every request, without repeating it in every single handler

This isn't an arbitrary list — it's grounded directly in why one of the most widely used frameworks on this entire list was built in the first place. TJ Holowaychuk built Express specifically because, in his own real, documented words, Node.js itself "lacked key features like routing, templating, middleware, and robust error handling" — three of this course's own four chosen capabilities named explicitly, by the person who built a framework specifically to provide them.

Not every real framework provides all four — and that's the actual difference between "micro" and "full-stack"
Express itself is the clearest real illustration of this: it ships genuinely robust routing and middleware, but no built-in ORM and no mandated templating engine at all — a view engine is opt-in and pluggable (EJS, Pug, or nothing), and data access is left entirely to whatever the developer chooses to add (Sequelize, Prisma, or raw SQL). Django takes the opposite real position, shipping a full ORM, a real templating engine, and a real middleware system all as one integrated package from the very first `pip install`. Neither choice is wrong — "micro-framework" versus "full-stack framework" is really just a label for exactly how many of this course's own four capabilities a given framework decides to ship as standard, versus leave as an explicit, deliberate integration point for something else.

Frameworks Are (Almost Always) Extracted From a Real Application

A recurring, genuinely verifiable pattern across framework history: the framework wasn't designed first, in the abstract, and then used to build something. It was pulled out of something real that had already been built, once its own author noticed the same problem recurring inside it.

FrameworkThe real, verified origin story
Ruby on RailsCreated by David Heinemeier Hansson in 2003 while building Basecamp, 37signals' own real project-management tool — Rails was extracted directly from Basecamp's own codebase and released as open source in July 2004, introducing "convention over configuration" as its own defining philosophy
DjangoBuilt starting in 2003 by Adrian Holovaty and Simon Willison (with Jacob Kaplan-Moss joining early) at the Lawrence Journal-World newspaper, to meet the real, fast-turnaround demands of a working newsroom — publicly released as version 0.90 on July 21, 2005
LaravelCreated by Taylor Otwell and first released in June 2011, built specifically as a real alternative to CodeIgniter, a then-popular PHP framework Otwell found genuinely lacking in built-in authentication and routing support
The same real pattern, three separate times
A working newsroom needed to publish stories fast (Django). A real project-management product needed a faster way to build its own next feature (Rails). An existing framework's own real, specific gaps — missing auth, missing routing — left one developer wanting something better (Laravel). None of these three frameworks started as "let's design the ideal web framework" in the abstract; each one started as a real, working application whose own author noticed the same handful of problems recurring, then generalized the solution into something reusable.

Frameworks This Course Draws On

Every comparison chapter in this course draws on real, working frameworks this site already has dedicated courses for — this course cross-references that material directly rather than re-teaching any single framework from scratch:

FrameworkLanguageThis site's own dedicated coverage
DjangoPythondjango1/django2, plus catalog-django1 and plpredict-django1 as full real-project builds
ExpressJavaScript (Node.js)express1/express2, plus react-food-tracker-1 and catalog-express1
Ruby on RailsRubyrails1/rails2, plus website-rebuild-with-rails1
LaravelPHPlaravel1/laravel2, plus website-rebuild-with-laravel1
FastAPIPythonfastapi1, fastapi-food-tracker-1, and plpredict-fastapi1
AstroJavaScript/TypeScriptastro1, plus website-rebuild-with-astro1
This course assumes basic familiarity, not mastery
Reading (or having already taken) at least one of the courses above helps ground the comparisons in something real rather than abstract, but this course explains every concept and every code sample from first principles — it doesn't assume any of them have been completed first.

Where This Course Is Headed

Routing — how it actually works (Chapter 2), then compared across Django, Express, Rails, Laravel, and FastAPI (Chapter 3); templating — how it actually works (Chapter 4), then compared across Django Templates/Jinja2, ERB, Blade, EJS/Pug, and JSX (Chapter 5); data access and the ORM layer — how it actually works (Chapter 6), then compared across the Django ORM, SQLAlchemy, ActiveRecord, Eloquent, and Prisma (Chapter 7); middleware and the request/response lifecycle — how it actually works (Chapter 8), then compared across Express, Django, Rails, and FastAPI (Chapter 9); and a closing capstone applying every prior chapter to a real, worked framework-selection decision (Chapter 10).

Hands-On Exercises

Exercise 1

Explain the real "who calls whom" distinction between a library and a framework, using one concrete example each from a framework this course will cover in later chapters (e.g. a Django view function vs. Python's own requests library).

📄 View solution
Exercise 2

Using TJ Holowaychuk's own real, quoted reason for building Express, explain why Express is still genuinely considered a framework rather than just a library, despite not shipping a built-in ORM or a mandated template engine.

📄 View solution
Exercise 3

Pick one of the three real framework origin stories from this chapter (Rails, Django, or Laravel) and identify, in your own words, the specific real problem its own author was trying to solve at the moment the framework was actually created.

📄 View solution

Chapter 1 Quick Reference

  • The real test — who calls whom: a library is called by your code, a framework calls your code (inversion of control)
  • Four core capabilities this course covers — routing, templating, data access, middleware, directly named in TJ Holowaychuk's own real reason for building Express
  • Micro vs. full-stack — really just a label for how many of these four a framework ships built in, versus leaves as a deliberate integration point
  • A real, recurring pattern — Rails (2004), Django (2005), and Laravel (2011) were each extracted from a real working application, not designed in the abstract first
  • Next chapter: Routing & URL Dispatch — how it actually works