Skip to main content
A pixel art illustration of a global open-source marketplace. Developers worldwide collaborate, contributing to and taking from stalls labeled Linux, Jackson, Spring, React. Commercial buildings above are supported by the OSS foundation. Tagline: Modern Software Stands on Open Source Shoulders.

CS 3100: Program Design and Implementation II

Lecture 23: Open Source Frameworks

©2026 Jonathan Bell, CC-BY-SA

Learning Objectives

After this lecture, you will be able to:

  1. Explain how component reuse simplifies software development and the role of OSS ecosystems in distributing reusable components
  2. Describe the role and impact of copyleft licenses on OSS
  3. Describe tradeoffs that should be considered when adopting a library or framework
  4. Evaluate a dependency's community health, governance model, and long-term viability

One Line of Code...

Modern software is not written from scratch. Consider adding JSON support to your project:

// build.gradle
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.0'
}

Three JARs for one "dependency." But this is just the beginning...

...But Real Apps Need More

Most apps need Java 8 date/time support and Optional handling. Add two more modules:

Notice the diamond dependencies — multiple paths to jackson-databind. Maven/Gradle resolves these, but version conflicts can cause subtle bugs.

Third-Party Dependencies Appear

Add XML and YAML format support — now third-party libraries enter the picture:

Three different organizations now contribute to your project: FasterXML (Jackson), Codehaus (Stax2), and org.yaml (SnakeYAML).

Thousands of Shoulders

Five lines in your build file. Ten JAR files from four organizations.

$ mvn dependency:tree
com.example:jackson-deps:jar:1.0-SNAPSHOT
├── jackson-databind:2.15.0
│ ├── jackson-annotations:2.15.0
│ └── jackson-core:2.15.0
├── jackson-datatype-jsr310:2.15.0
├── jackson-datatype-jdk8:2.15.0
├── jackson-dataformat-xml:2.15.0
│ ├── stax2-api:4.2.1 ← org.codehaus.woodstox
│ └── woodstox-core:6.5.1 ← com.fasterxml.woodstox
└── jackson-dataformat-yaml:2.15.0
└── snakeyaml:2.0 ← org.yaml

What you wrote:

  • 5 dependency declarations
  • ~20 lines of Java

What you're running:

  • 10 JAR files
  • 4 different organizations
  • Dozens of maintainers

Your JSON/XML/YAML handling stands on the shoulders of developers you've never met, at organizations you may not have heard of.

Infrastructure Code Gravitates Toward Open Source

Your competitive advantage isn't your JSON parser — it's your recommendation algorithm, your UX, your content.

LayerExamplesWho Builds It
DifferentiationRecommendation algorithms, UX, business logicYou (proprietary)
InfrastructureJSON parsing, web servers, encryption, databasesIndustry (open source)

Why would Netflix, Spotify, and Airbnb each employ engineers to independently build the same JSON parser? It makes far more sense to:

  1. Share the cost of development across the industry
  2. Benefit from bug fixes and security patches contributed by everyone
  3. Focus proprietary effort on what actually differentiates your product

The Cathedral and the Bazaar

Split illustration: Left shows a closed cathedral with small team working in isolation. Right shows a bustling bazaar marketplace with diverse developers trading code, filing issues, rapid releases. A developer walks from cathedral toward bazaar.

Open Source Won the Platform Wars

Timeline: IE (1995) kills Netscape, but Mozilla escapes with source code. Firefox rises (2004). Chrome/Safari launch with open source engines. Edge abandons proprietary engine for Chromium (2019). IE rusts abandoned. Callout: IE won the battle, open source won the war.

The Ecosystem: Package Registries

Open source ecosystems depend on package registries to distribute reusable components:

RegistryEcosystemExamples
Maven CentralJava/JVMJUnit, Spring, Jackson
npmJavaScriptReact, Express, lodash
PyPIPythonpandas, tensorflow, requests
crates.ioRustserde, tokio
NuGet.NETNewtonsoft.Json, Entity Framework

These registries make it trivial to declare a dependency and have it automatically downloaded — along with all of its transitive dependencies.

Semantic Versioning Encodes Promises

Libraries evolve. Semantic Versioning (SemVer) encodes compatibility promises in version numbers:

MAJOR . MINOR . PATCH

2 . 15 . 0

ChangeExampleMeaningSafe to Update?
PATCH2.15.0 → 2.15.1Bug fixes only✅ Yes
MINOR2.15.0 → 2.16.0New features, backwards compatible✅ Usually
MAJOR2.15.0 → 3.0.0Breaking changes⚠️ Review needed
// Exact version — reproducible but requires manual updates
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.0'

// Any 2.x version — automatic patches but risk of breakage
implementation 'com.fasterxml.jackson.core:jackson-databind:2.+'

The Double-Edged Sword of Dependency

Benefits

  • Speed: Don't reinvent the wheel; use battle-tested solutions
  • Quality: Popular libraries debugged by millions of users
  • Security: Vulnerabilities found and patched quickly (when maintained)

Risks

  • Supply chain attacks: Compromised dependency compromises your app
  • Abandonment: What happens when a maintainer stops updating?
  • License compatibility: Can you legally use this code?

We'll explore the risks — licensing and evaluation — in the rest of this lecture.

Every Line of Code Is Copyrighted by Default

To understand open source licensing, we first need to understand copyright.

Copyright is automatic. The moment you write code, you own exclusive rights to:

  • Copy the work
  • Modify it (create "derivative works")
  • Distribute copies to others
  • Display or perform it publicly

Key insight: A GitHub repo with no LICENSE file means "all rights reserved."

You legally cannot use it — even if the author clearly intended to share it.

From BSD to GPL: A Values Fork

The two licensing philosophies grew from the same code lineage — but diverged on a fundamental question about freedom.

YearEventPhilosophy
1969Bell Labs shares Unix freely (AT&T consent decree prevents selling it)Accidental openness
1977Berkeley builds on Unix, creates BSD license: "Do whatever you want, just credit us"Maximum individual freedom
1983Stallman sees companies closing BSD-derived source code. Launches GNU and GPL: "Share alike, forever"Protect the commons

Both philosophies shaped the modern world:

BSD lineage (permissive)GPL lineage (copyleft)
FreeBSD → macOS, iOSGNU/Linux → Android, Cloud
Companies took the code and closed it — as intendedCompanies must share kernel changes — as intended

The irony: BSD's freedom enabled companies to close the source — which is exactly what motivated Stallman to create a license that prevented it. Same code heritage, fundamentally different values.

Two Philosophies: Maximize Adoption vs. Protect the Commons

Two maker spaces in a shared warehouse. Left bench (BSD/MIT): blueprints freely available, someone takes one into a closed Proprietary Product door. Right bench (GPL): four workshop rules posted (use any tool, take apart machines, copy blueprints, share improvements) with a sign reading 'Free as in free speech, not as in free beer' and a Stallman-like figure gesturing proudly. Both benches are busy and productive.

Permissive vs. Copyleft: Quick Reference

QuestionPermissive (MIT, Apache, BSD)Copyleft (GPL, LGPL, AGPL)
Can I use it in proprietary software?YesUsually requires open-sourcing your code
Must I share my modifications?NoYes, if you distribute
PhilosophyMaximize adoptionProtect the commons
Popular examplesReact, Jackson, SpringLinux kernel, GCC, WordPress

Most popular libraries use permissive licenses: React (MIT), Jackson (Apache 2.0), Spring (Apache 2.0), TensorFlow (Apache 2.0)

GPL powers critical infrastructure: Linux kernel, many Linux utilities (maintained by GNU), forces community participation

Practical Licensing Guidance

For most developers using open source libraries:

  1. Check the license before adding a dependency. It's usually in a LICENSE file or package metadata.
  2. Permissive licenses are low-risk for most commercial projects.
  3. GPL requires careful attention if you're building proprietary software — consult your legal team.

Licenses as Values: The Unusual Cases

Three panels: (1) SQLite's public domain blessing on a scroll with a lawyer holding a Warranty of Title document, (2) JSLint's 'Good not Evil' license clause with a sweating lawyer and IBM's minions holding a golden 'Licensed for Evil' scroll, Crockford winking, (3) MongoDB's license tension with cloud providers facing off against SSPL requirements and the question: Open Source or Source Available?

Every Dependency Is a Commitment

Adding a dependency is easy — one line in build.gradle. But every dependency is a commitment.

Before adopting a library or framework, ask yourself:

  • Who maintains this, and will they still be maintaining it in two years?
  • What happens if I need to switch away from it later?
  • Am I comfortable with the license terms?
  • How much will this shape the rest of my code?

The last question matters most: A utility library for string formatting has minimal impact — swap it out easily. A web framework influences your entire application structure. Migrating away could mean a rewrite.

Community Health: The Most Important Factor

Before adopting a library, investigate who's behind it and how active they are:

IndicatorWhat to Look ForRed Flags
Recent activityLast commit? Last release?No updates in 2+ years
Issue responsivenessAre bugs acknowledged? PRs reviewed?500 ignored issues
Bus factorHow many core maintainers?Single maintainer
DocumentationUp-to-date? Comprehensive?Outdated or missing docs

Sobering reality: OpenSSL — securing most of the internet — was maintained by a handful of volunteers until the Heartbleed vulnerability exposed how underfunded critical infrastructure can be.

Governance Models Shape Long-Term Risk

ModelExamplesProsCons
Corporate-backedChromium (Google), React (Meta), TensorFlow (Google)Well-funded, full-time devsPriorities may shift; may change license
Foundation-governedApache projects, Linux Foundation, Python SFCommunity governance, stable long-termMay move slower
Community/IndividualMany small libraries and utilitiesInnovative, responsiveHigh abandonment risk; may lack security audits

Watch out for license changes:

  • MongoDB: AGPL → Server Side Public License (SSPL)
  • HashiCorp: Terraform from MPL → Business Source License (BSL)
  • Redis: BSD → dual-license with SSPL

When Governance Fails: The Fork

OriginalForkWhat Happened
MySQLMariaDBOracle acquired Sun (2009). Community worried about stewardship.
OpenOfficeLibreOfficeOracle fired developers (2010). Community forked. LibreOffice won.
TerraformOpenTofuHashiCorp changed to restrictive license (2023). Linux Foundation hosts the fork.

The code can live on — but the disruption is painful for everyone. Forks are possible because the source is open. The only thing original creators keep is the trademark.

The Human Side of Open Source

Behind every dependency in your build.gradle is a person — often unpaid, often alone.

The reality of OSS maintenance:

  • Most critical infrastructure runs on volunteer labor
  • OpenSSL (secures the internet): 2 maintainers pre-Heartbleed
  • Burnout is the #1 cause of project abandonment

L22's bus factor — at ecosystem scale:

  • Your team has 4 people. What if one leaves?
  • SnakeYAML has one maintainer. What if they leave?
  • The consequences ripple across thousands of projects
  • This isn't a team problem — it's an industry problem

The paradox: We trust billion-dollar systems to maintainers we've never met, often without paying them. The same HRT principles from L22 apply here — except the "team" is the entire open source community.

A Well-Maintained Library Beats DIY for Security

Is it safer to use a popular library or write your own? Almost always use the library — if it's well-maintained.

Why well-maintained libraries win:

  • Battle-tested by thousands of users
  • Security researchers scrutinize popular projects
  • Vulnerabilities get reported and patched
  • You benefit from specialists' expertise

When dependencies introduce risk:

  • Every dependency is an attack surface
  • Transitive dependencies multiply the risk
  • Unmaintained dependencies don't get patches
  • You're trusting code you've never read

The key insight: You're unlikely to out-engineer the Jackson team at JSON parsing, or the Spring Security team at authentication. But you DO need to keep your dependencies updated.

Supply Chain Attacks: When Dependencies Become Vectors

An attacker compromises a package deep in the dependency tree. Your app pulls it in transitively — you never knew it existed. Now the attacker's code runs in your application.

IncidentWhat HappenedImpact
left-pad (2016)Developer unpublished an 11-line npm packageThousands of builds broke — Facebook, Spotify, others
Log4Shell (2021)Critical remote code execution in Log4jMillions of apps affected; months to remediate

A chain is only as strong as its weakest link. The risks compound when you adopt a library and never update it, the library is abandoned, or a malicious actor compromises it.

Log4Shell: When a Logging Library Became an Attack Vector

In December 2021, a critical vulnerability in Log4j — a ubiquitous Java logging library — exposed millions of systems to remote code execution. This is the supply chain attack that changed how the industry thinks about dependencies.

The attack in plain English: Log4j had a "helpful" feature that let logged strings contain lookups like ${jndi:ldap://...}. If an attacker put this string anywhere your app logged — a username field, a search box, even a browser User-Agent header — Log4j would fetch and execute code from the attacker's server.

Why Nobody Saw It Coming: "It's Just Logging"

You wouldn't pass user input directly to dangerous operations. But logging doesn't feel dangerous.

OperationWould You Pass User Input Directly?
Runtime.exec("rm " + userInput)No way! User could type -rf / and delete everything
new File(userInput).delete()No way! User could type ../../../etc/passwd
log.info("User searched for: " + userInput)Sure, why not? It's just writing text to a file...

The trust assumption that broke: Every developer assumed log.info(userInput) was a sink — data goes in, gets written to a file, nothing else happens. Log4j made it a source of network requests and code execution. Logging became as dangerous as Runtime.exec() — but nobody knew.

Being a Good OSS Citizen

You're not just a consumer of open source — you're a participant. L22's HRT principles apply to the global community, not just your team.

Apply L22's practices to OSS:

  • File good issues — you learned this in L22. Same template: steps to reproduce, expected vs. actual, environment.
  • Look for "good first issue" labels — many projects tag beginner-friendly tasks. This is your on-ramp.
  • Contribute docs, not just code — the most impactful first contribution is often a typo fix or a clearer example
  • Respect maintainer time — they owe you nothing. A polite, well-researched issue gets answered; "fix this!" gets ignored.

Apply L22's HRT:

  • Humility: You're new to their codebase. Ask before assuming.
  • Respect: Read the CONTRIBUTING.md before submitting a PR. Follow their conventions, not yours.
  • Trust: If a maintainer closes your issue, assume good faith. Ask why, don't demand.

Start now: Every one of you uses open source daily. You can contribute today — report a bug, improve docs, answer a question on Stack Overflow. You don't need permission to participate in the bazaar.

Why the Bazaar Works: Modularity All the Way Down

The bazaar model only works because of information hiding. Every successful OSS library is a module with a clean boundary.

You don't know — and shouldn't care — how Jackson tokenizes JSON internally. That's the whole point. Information hiding is what lets strangers' code snap together like LEGO bricks.

Bad Boundaries Make OSS Adoption Impossible

If your code violates information hiding, you can't swap components — even when better alternatives exist.

Leaky boundary — locked in:

// Scattered throughout your codebase
JsonNode node = new ObjectMapper()
.readTree(input);
// Cast to Jackson-specific types
ObjectNode obj = (ObjectNode) node;
obj.put("key", "value");
// Use Jackson's streaming API directly
JsonParser parser = factory.createParser(input);
while (parser.nextToken() != null) { ... }

Jackson internals leaked everywhere. Switching to Gson? Rewrite the whole app.

Clean boundary — swappable:

// One place defines the contract
public interface JsonSerializer {
<T> String toJson(T object);
<T> T fromJson(String json, Class<T> type);
}

// Jackson implementation (hidden behind boundary)
class JacksonSerializer implements JsonSerializer {
private final ObjectMapper mapper =
new ObjectMapper();
// ... implementation details stay here
}

Switch to Gson? Write one new class. Nothing else changes.

L18's boundary heuristics told you: isolate things that change independently. OSS libraries change on THEIR schedule, not yours. That's exactly where a boundary belongs.

Modularity Works Both Ways

Good boundaries don't just help you consume OSS — they're what make your code reusable in the first place.

What makes a module extractable into a reusable library?

PrincipleWhat It Enables
Information hidingUsers don't depend on your internals — you can evolve freely
Clean interfacesUsers know exactly what to call and what to expect
Minimal dependenciesUsers don't inherit YOUR dependency tree
Single responsibilityUsers adopt only what they need — nothing extra

Every great OSS library started as a well-modularized internal component that someone realized others could use. Jackson, React, Kubernetes — all extracted from internal systems because the boundaries were clean enough to share.

The Virtuous Cycle: Modularity Enables the Ecosystem

Every principle we've studied this semester reinforces the others:

  • Information hiding lets strangers' code work together without understanding each other's internals
  • Clean boundaries let you swap one library for another when licenses change, maintainers disappear, or better options emerge
  • Modularity is what lets a single developer in Tokyo publish a component on Monday that teams worldwide adopt by Friday
  • Testability depends on all of the above — can you mock that dependency? Only if you injected it (L17) behind a clean interface (L18)

The bazaar doesn't work without modularity. And modularity doesn't matter without the bazaar to supply the components. They need each other. Every time you type implementation 'some:library:1.0', you're making an architectural decision. Evaluate it like one.