EbookFoundation/free-programming-books
:books: Freely available programming books
Mixed signals — read the receipts
- ✓Last commit 1w ago
- ✓5 active contributors
- ✓Distributed ownership (top contributor 30%)
- ✓CC-BY-4.0 licensed
- ✓CI configured
- ⚠Small team — 5 top contributors
- ⚠Non-standard license (CC-BY-4.0) — review terms
- ⚠No test directory detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Embed this verdict
[](https://repopilot.app/r/ebookfoundation/free-programming-books)Paste into your README — the badge live-updates from the latest cached analysis.
Onboarding doc
Onboarding: EbookFoundation/free-programming-books
Generated by RepoPilot · 2026-05-04 · Source
Verdict
WAIT — Mixed signals — read the receipts
- Last commit 1w ago
- 5 active contributors
- Distributed ownership (top contributor 30%)
- CC-BY-4.0 licensed
- CI configured
- ⚠ Small team — 5 top contributors
- ⚠ Non-standard license (CC-BY-4.0) — review terms
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
TL;DR
A community-curated, multi-language repository of links to freely available programming books, courses, and learning resources, organized into Markdown files under the books/ directory (e.g., books/free-programming-books-en.md, books/free-programming-books-zh.md). It solves the discovery problem for free technical education by providing a single, maintained, searchable index across dozens of human languages and hundreds of programming topics. A GitHub Pages static site and a separate search frontend (ebookfoundation.github.io/free-programming-books-search/) provide public access. Flat content-centric layout: all book lists live as individual Markdown files in books/ (one per language, e.g., books/free-programming-books-en.md), Python scripts in the repo root handle linting and validation, and .github/workflows/ contains all CI automation. There is no application server — GitHub Pages renders the site via _config.yml and _includes/head-custom.html.
Who it's for
Self-taught developers, students, and educators worldwide who want to find vetted, free programming books without hitting paywalls. Contributors are typically open-source enthusiasts and native speakers of non-English languages who add or curate resources in their language's specific Markdown file (e.g., books/free-programming-books-pt_BR.md for Brazilian Portuguese).
Maturity & risk
Extremely mature — it is one of GitHub's historically most-starred repositories, originating as a StackOverflow answer before moving to GitHub. CI is active via multiple GitHub Actions workflows (.github/workflows/check-urls.yml, fpb-lint.yml, rtl-ltr-linter.yml), and contributions are ongoing via Hacktoberfest 2025 stats shown in the README. Verdict: production-ready and actively maintained.
Very low risk for consumers — it is a static content repository with no runtime dependencies or security surface. The main risk for contributors is link rot (handled by .github/workflows/check-urls.yml) and PR merge conflicts, mitigated by .github/workflows/detect-conflicting-prs.yml. The project is steered by the EbookFoundation organization, reducing single-maintainer risk, but editorial quality depends entirely on volunteer reviewers.
Active areas of work
Active Hacktoberfest 2025 participation is explicitly called out in the README badge, meaning a high volume of PRs adding new book links are being reviewed. The .github/workflows/stale.yml workflow auto-manages stale issues, and dependabot (.github/dependabot.yml) is updating GitHub Actions dependencies. The rtl-ltr-linter.yml workflow suggests recent work on right-to-left language support quality.
Get running
git clone https://github.com/EbookFoundation/free-programming-books.git cd free-programming-books
No package install needed — content is pure Markdown
To run the Python linter locally:
pip install -r scripts/requirements.txt # if present python scripts/fpb_lint.py books/free-programming-books-en.md
To preview the GitHub Pages site locally:
gem install bundler jekyll bundle exec jekyll serve
Daily commands:
GitHub Pages (production): automatically deployed from main branch
Local Jekyll preview:
bundle exec jekyll serve --baseurl ''
Run URL checker locally (uses awesomebot via the custom action):
See .github/actions/awesomebot-gh-summary-action/action.yml for config
Run Python linter:
python scripts/fpb_lint.py <path-to-book-file>
Map of the codebase
README.md— Primary entry point and navigation hub linking all resource categories and language-specific lists; every contributor must understand its structure before adding links.books/free-programming-books-en.md— The largest and most active book list in English; establishes the canonical formatting conventions all other files must follow..github/PULL_REQUEST_TEMPLATE.md— Defines the required checklist every contributor must complete before submitting a PR, enforcing quality and formatting standards..github/workflows/fpb-lint.yml— Runs the custom linter on every PR to enforce link formatting, alphabetical ordering, and style rules — the main automated gatekeeper..github/workflows/check-urls.yml— Validates that all URLs in the lists are reachable; broken-link detection is critical for maintaining list quality.books/free-programming-books-langs.md— Language-agnostic programming books list organized by programming language rather than spoken language; key structural reference.books/free-programming-books-subjects.md— Subject-organized book list (e.g., algorithms, security, databases); demonstrates the subject-grouping convention used across the repo.
How to make changes
Add a new book to an existing language list
- Open the appropriate language-specific book file and locate the correct programming-language or subject section (sections are alphabetically ordered). (
books/free-programming-books-en.md) - Insert the new entry in alphabetical order within the section using the format:
* [Book Title](URL) - Author Name (format, year). Omit optional fields if unknown. (books/free-programming-books-en.md) - Review the PR checklist before submitting to ensure formatting, alphabetical order, and URL validity are correct. (
.github/PULL_REQUEST_TEMPLATE.md)
Add a new spoken-language book list
- Create a new Markdown file following the naming convention
free-programming-books-{lang_code}.mdmirroring the structure of an existing file. (books/free-programming-books-en.md) - Add the new language file link to the main README navigation section in alphabetical order among other language links. (
README.md) - Add the RTL linter workflow handling if the language is right-to-left (e.g., Arabic, Hebrew, Farsi). (
.github/workflows/rtl-ltr-linter.yml) - Verify the linter workflow covers the new file path pattern so CI checks apply to the new list. (
.github/workflows/fpb-lint.yml)
Add a new free course to a course list
- Open the appropriate language-specific course file and find the correct subject section. (
courses/free-courses-en.md) - Insert the course entry in alphabetical order using format:
* [Course Title](URL) - Instructor (platform, format)consistent with surrounding entries. (courses/free-courses-en.md) - Review the PR checklist to confirm the URL is live, the entry is alphabetically placed, and no registration wall exists. (
.github/PULL_REQUEST_TEMPLATE.md)
Add a new podcast or screencast
- Open the relevant language cast file and locate the correct technology or subject section. (
casts/free-podcasts-screencasts-en.md) - Add the entry in alphabetical order with format:
* [Title](URL) - Description (format)following existing conventions. (casts/free-podcasts-screencasts-en.md) - Submit PR with the checklist completed; CI will run URL checks and linting automatically. (
.github/PULL_REQUEST_TEMPLATE.md)
Why these technologies
- Markdown — Human-readable, diff-friendly plain text format that requires no build tooling for contributors to edit and is natively rendered by GitHub and Jekyll.
- GitHub Actions — Provides CI/CD automation (linting, URL checking, stale management) directly integrated with the GitHub PR workflow without external services.
- Jekyll / GitHub Pages — Zero-cost static site generation from Markdown files hosted directly on GitHub, requiring no server infrastructure or deployment pipeline.
- awesomebot — Purpose-built URL validation tool for awesome-list style repositories; validates HTTP responses and flags dead links automatically in CI.
- EditorConfig — Enforces consistent whitespace and line ending conventions across all editors and operating systems without requiring contributors to install additional tooling.
Trade-offs already made
-
Plain Markdown files instead of a database or CMS
- Why: Maximizes contributor accessibility — any text editor works, diffs are meaningful, and no account beyond GitHub is needed.
- Consequence: No programmatic querying, filtering, or structured data export; search must be handled by a separate external site.
-
Monorepo with all languages in one repository
- Why: Centralizes maintenance, linting, and community; PRs affecting multiple language lists can be reviewed together.
- Consequence: The repository is very large; PRs touching popular files (especially en.md) frequently have merge conflicts.
-
Alphabetical ordering enforced by linter
- Why: Ensures consistent, predictable organization and prevents arbitrary insertion order that degrades list usability.
- Consequence: Contributors must manually determine correct insertion point; linter failures are a common source of PR iteration.
-
CC BY 4.0 license for all content
- Why: Allows broad reuse, redistribution, and derivative works, maximizing the educational impact of the curated lists.
- Consequence: Any third party can republish the lists commercially as long as attribution is given; the project cannot restrict commercial use.
Non-goals (don't propose these)
- Hosting or storing the actual book/course content — only links to external resources are maintained
- Providing a built-in search interface — search is delegated to a separate external site (ebookfoundation.github.io/free-programming-books-search/)
- Verifying the quality
Traps & gotchas
The fpb-lint Python script enforces strict alphabetical ordering within sections and specific link formatting (e.g., author name placement, license annotations like '(PDF)', '(EPUB)') — PRs failing these checks are rejected by CI even if the content is valid. The awesomebot URL checker in check-urls.yml can produce false positives on sites with aggressive bot-blocking. RTL language files (ar, fa_IR, he) must not mix LTR/RTL markers incorrectly or the rtl-ltr-linter.yml workflow will fail. No environment variables or secrets are required for content contributions.
Architecture
Concepts to learn
- Awesomebot / link rot detection — The check-urls.yml workflow uses awesomebot to automatically detect broken hyperlinks across thousands of entries — understanding how HTTP-based link checkers work explains why some valid URLs get flagged.
- GitHub Pages Jekyll rendering — The static site is built directly from the books/ Markdown files by Jekyll via _config.yml with no build step — knowing how GitHub Pages auto-deploys from Markdown explains the zero-infrastructure publishing model.
- Unicode bidirectional algorithm (BiDi) — The rtl-ltr-linter.yml workflow enforces correct text directionality for Arabic, Hebrew, and Persian files — the Unicode BiDi algorithm governs how mixed-direction text is rendered and why incorrect markers break display.
- Stale bot / issue lifecycle automation — The .github/workflows/stale.yml workflow automatically closes inactive issues and PRs — understanding this pattern explains why open issues may be auto-closed without a maintainer response.
- Dependabot version pinning for Actions — The .github/dependabot.yml config keeps GitHub Actions versions updated automatically — this is a supply-chain security practice specific to CI/CD pipelines that new contributors modifying workflows should understand.
- Markdown linting as a contribution gate — The fpb-lint.yml workflow runs a custom Python linter that enforces alphabetical ordering, link format, and section structure — this pattern of machine-enforced style over human review is why PRs are rejected for non-content reasons.
Related repos
sindresorhus/awesome— The 'awesome list' meta-repo that inspired this project's format and from which it carries the Awesome badge.EbookFoundation/free-programming-books-search— The companion search frontend that indexes and makes all content in this repo dynamically searchable at ebookfoundation.github.io/free-programming-books-search/.vhf/free-programming-books— The original predecessor repository before it was transferred to the EbookFoundation organization.awesomedata/awesome-public-datasets— A similar community-curated Markdown link list in the same 'awesome' ecosystem, useful for understanding the contribution and curation patterns.jnv/lists— Aggregates many curated GitHub list repositories including this one, providing context for how free-programming-books fits into the broader open curation ecosystem.
PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Split books/free-programming-books-en.md into subject-grouped sub-files mirroring books/free-programming-books-subjects.md
The English list (free-programming-books-en.md) is almost certainly the largest single file in the repo — likely thousands of lines — making it hard to navigate, review PRs against, and maintain. A books/free-programming-books-subjects.md file already exists, proving the project has appetite for subject-based organisation. Splitting the English file into per-subject files (or at minimum creating anchored sections that mirror the subjects file) would reduce merge conflicts on PRs, make URL-check CI faster per-file, and improve contributor experience.
- [ ] Audit books/free-programming-books-en.md to identify the top-level subject headings already present
- [ ] Compare those headings against the structure used in books/free-programming-books-subjects.md to agree on a consistent taxonomy
- [ ] Create one new file per major subject group, e.g. books/free-programming-books-en-web.md, books/free-programming-books-en-algorithms.md, etc., following the existing filename convention
- [ ] Move entries from free-programming-books-en.md into the appropriate new files, preserving alphabetical ordering within each section as required by CONTRIBUTING guidelines
- [ ] Update README.md and any index/navigation links to point to the new files
- [ ] Verify .github/workflows/check-urls.yml and .github/workflows/fpb-lint.yml still pick up all new files (glob patterns) and adjust if needed
Add a GitHub Actions workflow to lint RTL-language book files (Arabic, Hebrew, Farsi, Urdu) separately from the existing rtl-ltr-linter workflow
The repo already has .github/workflows/rtl-ltr-linter.yml, which suggests RTL/LTR mixing is a known quality problem. However, the existing workflow appears to be a single monolithic check. The RTL book files (books/free-programming-books-ar.md, books/free-programming-books-fa_IR.md, books/free-programming-books-he.md, books/free-programming-books-ur.md) and cast files (casts/free-podcasts-screencasts-ar.md, casts/free-podcasts-screencasts-fa_IR.md, casts/free-podcasts-screencasts-he.md) should have a dedicated, path-filtered workflow so that a PR touching only Arabic content does not trigger a full-repo lint run, and so failures surface the exact offending file.
- [ ] Open .github/workflows/rtl-ltr-linter.yml and document its current trigger paths and scope
- [ ] Create .github/workflows/rtl-books-lint.yml with an 'on.push.paths' and 'on.pull_request.paths' filter covering books/free-programming-books-ar.md, books/free-programming-books-fa_IR.md, books/free-programming-books-he.md, books/free-programming-books-ur.md, casts/free-podcasts-screencasts-ar.md, casts/free-podcasts-screencasts-fa_IR.md, and casts/free-podcasts-screencasts-he.md
- [ ] Reuse or refactor the linting logic from the existing rtl-ltr-linter workflow into a shared composite action under .github/actions/ to avoid duplication
- [ ] Add a job step that outputs a per-file summary compatible with the existing .github/actions/awesomebot-gh-summary-action pattern so PR comments are consistent
- [ ] Test the new workflow against a sample PR that introduces a deliberate RTL/LTR mixing error in books/free-programming-books-ar.md and confirm it fails correctly
Add missing podcast/screencast files for languages that have book files but no casts file (e.g. German, Portuguese-BR, Russian, Chinese, Japanese, Korean)
undefined
Good first issues
- Audit books/free-programming-books-hi.md (Hindi) or books/free-programming-books-bn.md (Bengali) for dead links by running the URL checker locally — these smaller language files often have stale resources. 2. Add missing '(PDF)', '(EPUB)', or '(online)' format annotations to entries in books/free-programming-books-en.md that currently lack them, as the linter accepts but does not require them everywhere. 3. Verify and update the books/free-programming-books-my.md (Burmese) file, which is likely one of the smallest and least-reviewed language files and may have formatting inconsistencies.
Top contributors
- @margaret-hu — 7 commits
- @Muditapandey26 — 7 commits
- @dependabot[bot] — 3 commits
- @Thenlie — 3 commits
- @colmwoods — 3 commits
Recent commits
ecada4a— fix: restore College Mathematics with APL link in APL section (fixes #12901) (#13230) (Jah-yee)fc17167— AIPython: Python Code for understanding AI (#13224) (ghani-raheem)4936405— chore(deps): bump actions/github-script from 8 to 9 (#13222) (dependabot[bot])9a48cfc— Fix russian translate for CONTRIBUTING-ru.md (#13210) (CuminV)c27a20e— Add 'Learn Programming Logic with Games' (#13207) (kwdeveloper)d818a88— remove workflow_run conclusion check (#13190) (Thenlie)f026ade— Fix comment on PR workflow (#13188) (Thenlie)8206f6f— Fix LinkedIn share link in README.md (#13185) (23f2002020)ac223d3— Add Bootstrap 5 and IIT Madras Python cheat sheets (#13151) (24f2007780)fcea630— Fix typo: '3nd' → '3rd' in Automate the Boring Stuff entry (#13183) (samratpushpendra)
Security observations
The EbookFoundation/free-programming-books repository is primarily a static content and documentation project with a minimal attack surface. There are no hardcoded secrets, no backend application code, no database interactions, and no Docker infrastructure present. The main security considerations are operational and supply-chain in nature: reliance on third-party CDN assets (including a deprecated one), the potential for mutable GitHub Actions version pinning introducing CI/CD supply-chain risk, the community-driven nature of URL contributions which could introduce malicious links, and a lack of explicit Content Security
- Low · External Badge and Asset Loading from Third-Party CDNs —
README.md. The README.md references external resources from cdn.rawgit.com (deprecated CDN) and img.shields.io. The rawgit.com CDN is no longer maintained and has been shut down, which means the badge image could be replaced or serve unexpected content if the domain is ever re-registered by a malicious actor. Additionally, loading assets from third-party CDNs in web contexts introduces a supply-chain risk if those services are compromised. Fix: Replace cdn.rawgit.com references with a maintained CDN or self-host the badge assets. Use Subresource Integrity (SRI) hashes when referencing external resources in HTML contexts. Consider migrating to shields.io alternatives that are actively maintained. - Low · GitHub Actions Workflow Dependency Trust —
.github/workflows/check-urls.yml, .github/workflows/comment-pr.yml, .github/workflows/detect-conflicting-prs.yml, .github/workflows/fpb-lint.yml, .github/workflows/issues-pinner.yml, .github/workflows/rtl-ltr-linter.yml, .github/workflows/stale.yml. The GitHub Actions workflows reference third-party actions (e.g., in .github/workflows/ and .github/actions/). If these actions are pinned to mutable tags (e.g., @v2, @main) rather than specific commit SHAs, a compromised upstream action could execute arbitrary code in the CI/CD pipeline with access to repository secrets and tokens. Fix: Pin all third-party GitHub Actions to specific immutable commit SHAs (e.g., uses: actions/checkout@abc1234) rather than mutable version tags. Use Dependabot (already configured via .github/dependabot.yml) to keep these pinned versions updated automatically. - Low · Dependabot Configuration May Not Cover All Ecosystems —
.github/dependabot.yml. While a .github/dependabot.yml exists, the repository's full dependabot configuration is not visible. If GitHub Actions ecosystem scanning is not included, outdated or vulnerable action dependencies may go unnoticed. The project appears to lack a formal package dependency file, limiting automated vulnerability scanning coverage. Fix: Ensure the dependabot.yml includes the 'github-actions' ecosystem to monitor and auto-update GitHub Actions dependencies. Review and confirm all relevant package ecosystems are covered. - Low · Potential for Malicious URL Submission via Pull Requests —
books/, casts/, courses/ (all markdown files). The repository is a community-contributed list of URLs. Malicious contributors could submit links pointing to phishing sites, malware, or harmful content. The automated URL checker (.github/workflows/check-urls.yml) may only verify liveness, not the safety or legitimacy of content at those URLs. Fix: Supplement the URL liveness checker with reputation-based URL scanning (e.g., Google Safe Browsing API integration). Enforce mandatory human review for all URL additions via branch protection rules. Consider adding a content policy check to the CI pipeline. - Low · Custom HTML Include Without Content Security Policy —
_includes/head-custom.html. The file _includes/head-custom.html is included in the Jekyll-based GitHub Pages site. Without a defined Content Security Policy (CSP) meta tag or header, the site may be susceptible to cross-site scripting (XSS) if any user-controlled content were ever rendered, and third-party scripts or styles could be injected. Fix: Add a Content Security Policy meta tag within the custom head HTML to restrict allowed sources for scripts, styles, and other resources. Example: <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';">. Also consider adding X-Frame-Options and X-Content-Type-Options headers via the _config.yml or server configuration.
LLM-derived; treat as a starting point, not a security audit.
Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.