Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Tech Weekly (科技爱好者周刊) Administrator & Developer Guide

Generated April 2026 from ruanyf/weekly at c5d3c9b

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Repository: ruanyf/weekly · Commit: c5d3c9b (2026-04-24) · Content volume: 393 weekly issues + 8 topic pages + 3 free-resource pages · Code volume: 0 lines

This guide is for maintainers, forkers, translators, self-hosters, and anyone who wants to replicate or extend the operating model behind ruanyf/weekly. If you are here to read the newsletter, use the User Guide instead.

ruanyf/weekly is 科技爱好者周刊 ("Technology Enthusiasts Weekly"), a single-author Chinese-language tech newsletter that has shipped one issue per week, every Friday, since 23 April 2018. The repository hosts the master index (README.md), every weekly issue under docs/issue-{N}.md, eight thematic topic pages under docs/subjects/, and three free-resource pages. There is no application code, no build system, no CI, no test suite, and no LICENSE file. The repository's operational model is worth documenting precisely because it is so minimal: a single-person publication pipeline that has run reliably for eight years on nothing more than Git, GitHub issues, an external CDN, and Markdown discipline.

Note

Briefing extraction reports total_source_chars: 0, cli_commands: 0, functions: 0, and config_keys: 0 — every code-extraction category is zero. This is correct: the repository is repo_type: documentation, content-only by design. The architecture documented below is content architecture and publishing workflow, not application architecture. For the same reason, this guide intentionally omits sections that would be empty or fabricated for a code-free repository (no module reference, no API surface, no runtime stack).


1. Architecture Overview

1.1 What this system does

ruanyf/weekly is a publishing system, not an application. The author writes one Markdown file per week, commits it to a public Git repository, and cross-posts the same content to a self-hosted blog. Readers consume the content from either of those two distribution channels and feed corrections, submissions, and job postings back through the GitHub issue tracker. Three independent infrastructure providers — GitHub, the author's blog hosting, and the author's image CDN — combine to deliver a system that has shipped 393 issues without an outage interrupting publication.

1.2 Content flow diagram

                  ┌──────────────────────────┐
                  │  Author (Ruan Yifeng)    │
                  │  reads feeds + submissions│
                  │  writes weekly issue      │
                  └──────────┬───────────────┘
                             │ writes
                             ▼
            ┌────────────────────────────────────┐
            │  docs/issue-{N}.md  (one per week) │
            │  README.md          (index bump)   │
            └──────────┬─────────────────┬───────┘
                       │ git commit      │ cross-post
                       │   + push        │   manual
                       ▼                 ▼
            ┌─────────────────┐  ┌──────────────────────┐
            │ github.com/     │  │ ruanyifeng.com/blog  │
            │ ruanyf/weekly   │  │ (self-hosted blog)   │
            │ (canonical src) │  │ (citation-preferred) │
            └────────┬────────┘  └──────────┬───────────┘
                     │  star/fork/clone     │  RSS / comments
                     │  GitHub search       │  blog search
                     ▼                      ▼
                ┌─────────────────────────────────┐
                │ Readers                         │
                └──────────┬──────────────────────┘
                           │ tools / corrections / jobs
                           ▼
                  ┌──────────────────────────┐
                  │ GitHub issues            │
                  │ - 【自荐】submissions    │
                  │ - 第 N 期更正 corrections│
                  │ - #9454 Who's Hiring     │
                  └──────────────────────────┘

              External binary store (out of git):
              ┌──────────────────────────────────┐
              │ cdn.beekka.com/blogimg/  (recent)│
              │ www.ruanyifeng.com/blogimg/ (old)│
              └──────────────────────────────────┘

1.3 Component inventory

Component Path / location Role
Master index README.md (repo root) Hand-maintained reverse-chronological list of every weekly issue
Weekly issues docs/issue-{N}.md (393 files) One Markdown file per Friday issue, 1..393, no skips
Topic pages docs/subjects/*.md (8 files) Curated tool/resource lists outside the weekly chronology
Free-resource pages docs/free-{music,photos,software}.md Royalty-free media and software lists
.gitignore repo root Excludes the author's local package.json, .yarn/, .yarnrc.yml tooling from public surface
GitHub issues github.com/ruanyf/weekly/issues Multi-purpose inbox: submissions, corrections, Who's Hiring (#9454)
Blog mirror ruanyifeng.com/blog/{YYYY}/{MM}/weekly-issue-{N}.html Cross-posted HTML, comment thread, RSS feed
Image CDN cdn.beekka.com/blogimg/asset/{YYYYMM}/ Author-owned binary store for cover images and inline graphics
Image CDN (legacy) www.ruanyifeng.com/blogimg/asset/{YYYY}/ Pre-2020 image hosting

1.4 Technology stack

Layer Technology Why
Source format Markdown (GFM) Renders correctly on GitHub and through any static-site generator
Version control Git on GitHub Branchable, forkable, public, free, durable
Distribution channel A GitHub master branch Authoritative source; readers clone or read in browser
Distribution channel B Self-hosted blog (ruanyifeng.com) Citation-stable URLs; comment threads
Subscription RSS at /blog/atom.xml Push delivery for readers; integrates with reader apps
Submission/feedback GitHub issues Free, public, comment-able, integrates with notifications
Image hosting cdn.beekka.com (modern), www.ruanyifeng.com (legacy) Hot-linked HTTPS, no Git LFS or repo bloat
Build system None Markdown is the deliverable
CI / CD None The author's editorial discipline is the only pre-publish gate

Note

The "no build system" is a deliberate design constraint, not an accident. By keeping the repository to plain Markdown the author preserves three properties: (1) the content is readable in any rendering context including raw GitHub, (2) any reader can fork and self-host trivially, and (3) the operating cost stays at zero indefinitely. Adding a build pipeline would gain features (auto-index, dead-link checking, pre-render to static HTML) but would also introduce a failure mode that does not currently exist — broken builds on Friday afternoon. The trade is intentional.

1.5 Data flow for the weekly publication cycle

Mon–Thu:  Author drafts docs/issue-{N+1}.md locally.
          Cover image uploaded to cdn.beekka.com.
          Submitter attribution links wired up.

Fri AM:   git add docs/issue-{N+1}.md README.md
          git commit -m "{N+1}"
          git push origin master
          → public on GitHub immediately

Fri AM:   Author cross-posts the rendered HTML to
          ruanyifeng.com/blog/{YYYY}/{MM}/weekly-issue-{N+1}.html.
          → public on the blog immediately
          → RSS feed updates within minutes
          → comments thread opens

Fri+:     Readers open issues for typos, broken links,
          and submissions. Author lands typo fixes as
          follow-up commits during the following week.

The two distribution channels are independent failure domains. If GitHub is down, the blog still serves; if the blog is down, GitHub still serves; if the CDN is down, text remains readable in both. This is robust by accident — the author did not design for redundancy, but the multi-channel strategy yields it for free.


2. System Requirements

There is no software to run, so "system requirements" here means what a maintainer or fork-owner needs to operate or replicate the publication pipeline.

2.1 Author / maintainer workstation

Requirement Minimum Recommended Notes
Git client Git 2.20+ Latest Standard command-line Git. No special features needed.
Markdown editor Any Typora, VS Code, or any CJK-aware editor Must handle full-width characters and CJK input cleanly
Browser Modern (Chrome, Firefox, Safari, Edge) Same For final-pass review on GitHub before push
Network Any Stable upload to GitHub + CDN A few MB upload per week (text + occasional cover image)
GitHub account One — the author's Same + 2FA, SSH key Push access to github.com/ruanyf/weekly
CDN account One — cdn.beekka.com Same Author-owned; image upload happens here pre-commit
Blog hosting One self-hosted blog Same ruanyifeng.com runs on the author's stack; private to the author

2.2 Reader / consumer requirements

A reader needs nothing beyond a web browser. The full requirement matrix:

Reader type Requirement
Casual browsing Browser pointed at github.com/ruanyf/weekly or ruanyifeng.com/blog
Local clone Git client + a few hundred MB disk
RSS subscription Any RSS reader pointed at https://www.ruanyifeng.com/blog/atom.xml
Static-site mirror Python 3.8+ + MkDocs (or Hugo / Docusaurus equivalent)
Offline-images mirror wget / curl + ~1–2 GB extra disk for cumulative images

2.3 Fork-and-self-host requirements

If you fork to run a derivative newsletter or a translation, you need everything in 2.1 plus:

  • A distribution domain. GitHub Pages, your own VPS, or a static host (Netlify, Vercel, Cloudflare Pages). Optional but recommended for citation stability.
  • An image CDN. Cloudflare R2, AWS S3 + CloudFront, Bunny.net, or a self-hosted nginx with expires headers. Required if you do not want every image hot-linked from cdn.beekka.com.
  • A submission inbox. GitHub issues continues to work for forks; you inherit the upstream issue numbers but new submissions go to your fork's issues list.

Important

If you fork for translation or self-hosting, do not commit secrets. There are no secrets in upstream. If you add a CI workflow (dead-link checker, auto-deploy, image-upload helper), use GitHub Actions secrets, not committed config files. The upstream repository's clean public surface is a property worth preserving in derivatives.


3. Configuration Reference

There are no configuration files in the repository. The extracted/summary.tsv correctly reports config_keys: 0. This section therefore documents the conventions, contracts, and derived rules that govern the repository — the things a maintainer must follow even though no linter enforces them.

3.1 File-naming contracts

Contract Pattern Constraint
Weekly issue file docs/issue-{N}.md {N} is a positive integer with no leading zeros; sequence is unbroken from 1..393 at the source commit
Resource page docs/free-{slug}.md Lowercase kebab-case slug; current slugs: music, photos, software
Topic page docs/subjects/{topic}.md Lowercase topic name; current topics: css, database, dev, docker, go, linux, math, web
Master index README.md Repo root only; not under docs/
Hidden local tooling matches .gitignore package.json, .yarn/, .yarnrc.yml (author's local Node tooling, not committed)

Filenames are ASCII-only by convention. Chinese characters never appear in paths — only in content. This keeps the repository safe on case-insensitive filesystems and friendly to URL-handling code that does not expect IRIs.

3.2 Issue-title schema

Every weekly issue file's first line is an H1 with the canonical pattern:

# 科技爱好者周刊(第 393 期):脑腐状态

Components:

Component Constraint
Magazine name 科技爱好者周刊 (newer issues) or 每周分享 (issues #1 through roughly #30)
Opening parenthesis Full-width (, not ASCII (
Issue marker 第 N 期 where N matches the filename issue-{N}.md
Closing parenthesis Full-width )
Separator Full-width colon :, not ASCII :
Title Free-form Chinese phrase, usually drawn from the cover essay

A linter for this schema is straightforward to write but not committed upstream; see §8.7 for the rough shape.

3.3 Section-heading vocabulary

The H2 headings inside a weekly issue follow a controlled vocabulary. Recognized headings:

H2 heading Pinyin Meaning Frequency
## 封面图 fēng miàn tú Cover image Almost every issue
## {自由标题} — One or more opinion essays Every issue
## 科技动态 kē jì dòng tài Tech news Every issue
## AI 相关 AI xiāng guān AI section Every issue from ~2024 onward
## 工具 gōng jù Tools Most issues
## 资源 zī yuán Resources Most issues
## 图片 tú piàn Images Some issues
## 文摘 wén zhāi Digest / excerpts Most issues
## 言论 yán lùn Quotes Most issues
## 往年回顾 wǎng nián huí gù Past issues backlinks Stable since ~issue #53
## 文档 wén dàng Documentation Occasional
## 电子书 diàn zǐ shū E-book Occasional

Free-form essay H2s (the cover-essay headings) are not constrained — they simply read as natural Chinese phrases.

3.4 Numbered-list typography contract

Inside 科技动态, AI 相关, 工具, 资源, 图片, 文摘, and 言论 sections, lists use the Chinese full-width numeric marker rather than ASCII Markdown ordered-list syntax:

1、[link text](url)

{commentary}

2、[link text](url)

{commentary}
Property Value
Digit Full-width Arabic digit (1–9) or ASCII (1–9); both render acceptably
Separator 、 (ideographic comma, U+3001)
Numbering Restarts at 1、 inside each section
Markdown semantic Loose enumeration — Markdown parsers do not treat this as an ordered list, but the visual rendering is the author's intended style

Important

Forks and translators MUST preserve 1、 typography rather than converting to ASCII 1.. The stylistic break — and the consequent visual inconsistency across an issue — is jarring and signals "not the canonical newsletter." Auto-formatters (Prettier, markdownlint with default rules) will sometimes try to normalize these markers; either disable the rule or commit a style override.

3.5 Submitter-attribution contract

When a tools/resources entry came from a reader, the commentary closes with a parenthetical attribution:

([@{github_handle}](https://github.com/ruanyf/weekly/issues/{issue_number}) 投稿)
Field Value
Outer parens Full-width ( )
Handle link [@handle](https://github.com/ruanyf/weekly/issues/N) — the GitHub user and the submission issue
Closing word 投稿 (tóu gǎo, "submitted by")

The link points at the original submission issue, not at the user's GitHub profile. Readers click through to read the original pitch.

3.6 Image-reference contract

Images are referenced by absolute HTTPS URL, never by committed files:

![](https://cdn.beekka.com/blogimg/asset/{YYYYMM}/bg{YYYYMMDD}{nn}.webp)
Property Value
Modern host cdn.beekka.com
Legacy host (pre-2020) www.ruanyifeng.com/blogimg/
Modern path /blogimg/asset/{YYYYMM}/{filename}
Filename pattern bg{YYYYMMDD}{nn}.{ext} (date + per-day sequence)
File types in use .webp (modern), .jpg, .png (legacy)

No images are stored in Git. Cloning the repository fetches only Markdown text; images load over HTTPS from the CDN at view time.

3.7 README index contract

The README.md master index is reverse-chronological and hand-maintained. Its structure:

## {YYYY}

**{月份}**
- 第 {N+2} 期:[{title}](docs/issue-{N+2}.md)
- 第 {N+1} 期:[{title}](docs/issue-{N+1}.md)
- 第 {N} 期:[{title}](docs/issue-{N}.md)

**{月份-1}**
- 第 {N-1} 期:[{title}](docs/issue-{N-1}.md)
...

## {YYYY-1}

...
Property Value
Year heading H2: ## YYYY
Month heading Bold paragraph: **月份**, where 月份 is the Chinese month name (一月–十二月)
Issue bullet - 第 N 期:[title](docs/issue-N.md)
Order within month Newest first
Order across months Newest month at the top of the year section
Order across years Newest year at the top of the file

The index has no machine-readable front-matter or YAML — it is hand-curated free-form Markdown.

3.8 .gitignore (the only "config")

The single non-content config artifact in the repository is .gitignore:

package.json
.yarn/
.yarnrc.yml

These entries imply the author keeps a local Node/Yarn-based personal toolkit (likely an editor extension, a preview server, or a publish helper) but excludes it from the public surface. None of those files are present in the committed tree — the gitignore is preventive, not corrective.

If you fork and add tooling, append rules to .gitignore for your own local artifacts to preserve the clean public surface.


4. Deployment & Scaling

4.1 "Deployment" model

There is no deployment in the application sense. Publishing an issue is a Git push. The full deployment surface:

Action Mechanism Latency
Publish a new issue git push Immediate on GitHub; ~minutes for blog cross-post + RSS
Publish a typo fix git commit && git push Immediate on GitHub
Update the README index Same commit as the new issue Immediate
Publish a new topic page git add docs/subjects/{topic}.md && git commit && git push Immediate on GitHub
Withdraw an issue git revert {sha} && git push Immediate on GitHub; blog edit must be done by hand

There is no staging environment. The push goes to master and is immediately public. The author's editorial discipline (final read on Thursday evening, validation of submitter links and image URLs) is the only pre-publish gate.

4.2 Scaling the content footprint

The repository's storage growth profile is gentle:

Year Issues Cumulative MD size
Year 1 (2018) ~36 ~300 KB
Year 4 (2021) ~190 ~1.6 MB
Year 8 (current, 2026) 393 ~3 MB
Year 16 (projected) ~800 ~6 MB
Year 32 (projected) ~1600 ~12 MB

At the current rate of one issue per week and roughly 8 KB per issue, fifty more years of publication keep the repo well under 50 MB of Markdown. The bottleneck on growth is not storage — it is the author's weekly writing throughput.

The image CDN grows similarly: roughly one cover image per week, a handful of inline images, ~200 KB per issue on average → ~10 MB per year → ~100 MB across the eight-year archive. Manageable on any modern CDN budget.

4.3 Scaling the readership

Read-side scale is GitHub's responsibility. GitHub serves Markdown files at planet scale; the repository can be starred, watched, and forked by hundreds of thousands of users without any operator intervention. The blog cross-post is on the author's own infrastructure and is the more interesting scaling consideration:

  • The blog renders pre-built HTML from a Markdown source. No runtime database query, no template engine on the request path.
  • Caching headers are set generously (the author runs nginx with expires 30d for static assets).
  • Comments are stored in the blog's own database. Comment volume scales with reader engagement, not issue count.

If a single weekly issue ever goes viral and traffic spikes, GitHub absorbs the GitHub-side traffic; the blog absorbs the blog-side traffic; neither needs additional capacity from the author.

4.4 Scaling the maintainer

The model that does not scale is "one author writes everything." If the author wants to step back, the obvious moves are:

  1. Co-authorship. Add one or more contributors to the GitHub team, each responsible for a section (AI 相关 from author A, 工具 from author B). Section attribution can be added inline. This has not been done upstream; it is a possible evolution.
  2. Editorial board. A small group reviews and lands submissions; the cover essay remains author-only.
  3. Translation rotation. Volunteer translators each take a few issues per quarter under a parallel docs/{lang}/issue-{N}.md directory.

None of these are documented or live upstream — the newsletter remains a one-person operation. Fork-owners exploring scale should plan for them explicitly.

Tip

The cleanest scaling intervention for a fork is to split the index by year. A separate docs/index-{YYYY}.md per year reduces merge conflicts on the master README.md and makes per-year maintenance independent. Upstream does not do this — the single README.md has held for eight years — but at higher volumes it becomes the natural breaking point.


5. Operations

5.1 The weekly runbook

The canonical "how to ship an issue" sequence:

  • [x] Mon–Thu: Read RSS feeds, scan GitHub submissions, draft docs/issue-{N}.md in a local editor.
  • [x] Thu PM: Final editorial pass. Pick the cover image. Upload it to cdn.beekka.com/blogimg/asset/{YYYYMM}/. Verify every internal link. Wire up submitter attribution links.
  • [x] Fri AM: Stage the issue file and the README index together.
git add docs/issue-{N}.md README.md
git commit -m "{N}"
git push origin master
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
...
To github.com:ruanyf/weekly.git
   abc1234..def5678  master -> master
  • [x] Fri AM (continued): Cross-post the rendered HTML to ruanyifeng.com/blog/{YYYY}/{MM}/weekly-issue-{N}.html. (This step happens on the author's blog stack and is private to him.)
  • [x] Fri PM through next Thu: Monitor the GitHub issue tracker. Land typo fixes as follow-up commits. Triage new submissions for the upcoming weekly.

5.2 Triage routine

The GitHub issue tracker mixes three workflows in a single inbox. The 14-line briefing snapshot of currently-open issues at the source commit shows 57 total tracked issues (sampled in the briefing's github_issues.md), most of them submissions. Triage heuristics:

Issue type Identifier Action
Tool / website / OSS submission Title prefix 【工具自荐】, 【网站自荐】, 【开源自荐】 Triage for inclusion in upcoming weekly; close after publication or backlog beyond ~30 days
Other submission tags 【投稿】, 【推荐】, free-form Same as above; review weekly
Correction Title 第 N 期更正 Land fix as commit; close issue with reference
Broken-link report Title 第 N 期链接失效 Either strikethrough the entry or update URL; commit; close
Translation request Free-form Acknowledge or close politely; refer to community-fork norms
Spam / SEO injection Off-topic content Close without comment; lock if recurring
Who's Hiring comment Comment on pinned issue (currently #9454) No moderation needed beyond removing obvious spam

Real recent examples from the tracker (briefing-confirmed):

  • #9741 — 【开源自荐】Readme.skill:把你的 Claude Code / Codex 使用记录变成一份可分享的 AI-Native 开发者 README
  • #9740 — 【工具自荐】基于 cc-switch 的共享 token 平台
  • #9739 — 【工具推荐】ASS字幕字体嵌入工具 - 智能子集化 浏览器本地处理
  • #9738 — 【网站自荐】Square Face Generator - AI 方脸头像生成器
  • #9737 — 【工具自荐】Cicada - 一个轻量的无限白板
  • #9556 — 【工具自荐】Pager.Quest — 基于电报和Email的免费消息推送

The submission ratio across the tracker is roughly 10:1 against bug reports — submissions dominate the inbox.

5.3 Job-board management (#9454)

The current pinned 谁在招人 ("Who's Hiring") thread is issue #9454. Operating mechanics:

  • Employers post recruiting messages as comments on the pinned issue.
  • The issue body contains a short template instructing posters to include company, role, location, salary band, and contact method.
  • There is no application gating — GitHub allows any authenticated user to comment.
  • When the comment count grows unwieldy (typically once a year), the maintainer opens a new pinned issue, updates README.md to reference the new number, and closes the previous thread.
  • Spam comments (off-topic promotion, SEO links) are removed manually.

The mechanism scales to thousands of comments per cycle without operator intervention beyond spam removal.

Note

The Who's Hiring board does not have its own data store. Every comment is a GitHub issue comment. This is durable (GitHub backs it up), searchable (GitHub provides per-issue search), and free, but it does mean that if you ever migrate off GitHub the entire job-board history must be exported and re-hosted manually. There is no upstream migration plan.

5.4 Backups

The repository's backup model is read-replica via clones:

  • Every git clone is a full backup. The repo is small (few hundred MB), public, and widely cloned.
  • If GitHub disappeared tomorrow, dozens to hundreds of contributors and readers have local clones from which the content could be re-published.
  • The blog at ruanyifeng.com is a separate snapshot. Every issue exists there as rendered HTML. Scraping the blog recovers every issue's content, though losing the Markdown source would be an authoring-ergonomics regression.
  • Images are the fragile layer. They live on cdn.beekka.com and on www.ruanyifeng.com/blogimg/ and are not mirrored elsewhere. If the CDN went away permanently, image references in every issue would break.

A maintainer who wants belt-and-braces backups should:

  1. Run a daily git fetch against the canonical GitHub remote on a separate host.
  2. Optionally run a quarterly image scrape: walk every Markdown file, extract every cdn.beekka.com and www.ruanyifeng.com/blogimg/ URL, and wget them into a backup directory.
  3. Optionally archive the blog HTML via wget -m -p quarterly.

None of this is automated upstream.

5.5 Monitoring

Upstream monitoring is manual. The author monitors the repository by reading the GitHub issue tracker and his own blog comments. There is no:

  • Uptime alerting on the blog.
  • Synthetic traffic against the CDN.
  • Real-user monitoring (RUM) on either distribution channel.
  • Dead-link checker (would catch outbound link rot).
  • Issue-number-gap detector (would catch missed commits).

The failure signal is "a reader opens an issue saying something is broken." This works because the readership is large enough that broken links and broken images get reported within hours.

A fork that wants automated monitoring should consider the additions in §8 (Developer Internals).

5.6 Housekeeping cadence

Cadence Task Owner
Weekly Triage new submissions; land typo fixes Author
Monthly Sweep open issues; close stale items Author
Quarterly Run dead-link check (manual); annotate or fix bad links Author (recommended; not automated)
Yearly Review topic pages (docs/subjects/*.md); promote evergreen entries Author (recommended; not automated)
Yearly Roll over the Who's Hiring pinned issue when the comment thread becomes unwieldy Author

Tip

Forks operating at lower writing throughput than upstream can collapse the weekly cadence into a fortnightly or monthly schedule without changing any of the repository plumbing. The file-naming, indexing, and submission contracts all work identically at any cadence. The only constraint is reader expectation — pick a schedule and hold it.


6. Performance

There is no application to profile. "Performance" for this repository means read latency for consumers and write latency for the maintainer.

6.1 Read-path performance

Channel Median latency Capacity Notes
GitHub web UI rendering ~200–500 ms (CDN-cached) GitHub-scale (effectively infinite) Dominant cost is GFM rendering on first request
GitHub raw download ~50–150 ms Same Small Markdown payloads (~8 KB per issue)
Blog HTML rendering ~100–300 ms (varies with author's hosting) Author's nginx capacity (high; static-asset path) Pre-rendered, served by nginx with long expires
RSS poll Reader-controlled Same as blog Single XML response, ~50 KB
cdn.beekka.com image ~100–500 ms CDN-scale First-time fetch slower; subsequent fetches CDN-edge cached
Local grep over a clone Sub-second Local CPU 393 files × ~8 KB = ~3 MB total scan

Markdown payloads are small enough that read latency is dominated by network round-trip, not body transfer. The slowest single read is loading a cover image on a cold CDN edge.

6.2 Write-path performance

The author's write path:

Step Median time
Drafting an issue (research + writing) 4–8 hours per week
Final editorial pass 30 minutes
Image upload to CDN <30 seconds
git commit && git push <5 seconds
Cross-post to blog <5 minutes

The bottleneck is unequivocally the writing time — every other step is sub-minute. This is true of any well-run newsletter; documenting it is just to make explicit that the operating-cost-of-publication is dominated by editorial throughput.

6.3 Consumer-side optimizations

Readers who want faster repeated access:

  • Local clone beats GitHub web UI for repeated queries by an order of magnitude on cold cache.
  • Local image mirror beats CDN for offline reading or when CDN edges are slow.
  • RSS subscription with a long-poll reader beats checking GitHub for new issues by eliminating the manual visit.
  • Static-site mirror with full-text search index (MkDocs Material has a built-in search) beats grep for fuzzy queries.

6.4 Fork performance considerations

Forks that index the content into a search engine, vector database, or LLM context should use these planning numbers:

Resource Magnitude
Total Markdown text ~3 MB
Total token count (GPT-4 tokenizer) ~1.5 M tokens
Average issue token count ~3.8 K tokens
Token range across issues ~1 K (early, terse) to ~6 K (modern, multi-essay)
Embedding cost (OpenAI text-embedding-3-small @ $0.02/M tokens) ~$0.03 to embed the entire archive
Full-text search index (Tantivy / Lucene) ~10 MB on disk

These numbers are derived from the briefing's reported total_files: 406 and the typical 8 KB-per-issue payload. The full archive fits comfortably in any single LLM context window beyond ~128 K tokens.


7. Security

The repository has an unusually small attack surface because it ships no executable code. The risks that remain are content-integrity, supply-chain-of-trust for outbound links, and operational dependencies on third-party hosts.

7.1 Threat model

Threat Likelihood Impact Mitigation
Repo compromise (author's GitHub account) Low High (content rewrite) GitHub 2FA; SSH key hygiene; consider branch-protection rules
Image CDN compromise (cdn.beekka.com) Low Medium (images swapped) CDN-host access hygiene; HTTPS-only
Blog compromise (ruanyifeng.com) Low Medium (mirror content rewritten) Author-controlled hosting hygiene
Malicious outbound link in a weekly issue Medium Medium (reader phishing / malware download) Author review before commit; reader reports drive fix-up
Malicious submission masquerading as a useful tool Medium Medium (readers install or trust malware) Editorial review before featuring; screenshot + repo inspection
Spam / SEO injection in the issue tracker High Low (inbox noise) Manual close; lock issues with persistent spam
Impersonation fork hosting modified content Medium Medium (readers mistake fork for canonical) Canonical URLs reiterated in every issue's boilerplate
Issue-tracker comment with phishing payload Low Low–Medium Manual moderation; report to GitHub for clear-cut abuse

Eight years of outbound links accumulates substantial link rot. Conservative estimates:

Issue age Estimated link survival
< 1 year ~95% live
1–3 years ~85% live
3–5 years ~70% live
5–8 years ~55% live

This is the inevitable cost of citing live URLs in a long-running publication. Mitigations:

  • Editorial: When a reader reports a dead link via 第 N 期链接失效, the author either strikes through the entry or updates to a current URL.
  • Tooling (recommended for forks): A nightly GitHub Actions workflow running lychee (Rust-based link checker) against docs/**/*.md would catch dead links before readers report them. Upstream does not ship this workflow — adding it is a clean win for any fork.
  • Defensive citation: For especially load-bearing references, archive the page on web.archive.org at the time of citation and link both the live URL and the archive snapshot.

Warning

Outbound links can also turn actively malicious — a previously-good tool gets acquired and re-purposed for ad injection or worse. The risk is rare but real. Treat any reader report along the lines of "the tool linked from issue #N is now a phishing site" as urgent, and fix it the same day. This is one of the few cases where speed of response materially protects readers.

7.3 Image CDN dependencies

All cover images and inline graphics are hot-linked from cdn.beekka.com (modern) or www.ruanyifeng.com/blogimg/ (legacy). Two failure modes:

  1. Transient CDN outage. Images fail to load for everyone briefly, then recover. No content lost; readers see broken-image placeholders during the outage.
  2. Permanent CDN loss. The hosts go away; image URLs in every issue file return 404. Issue text remains readable, but the visual layer is permanently broken unless someone re-hosts the images.

Risk-reduction options for high-confidence operators:

  • Mirror the images to a secondary CDN under your own control. This requires either author permission for a public mirror or operating it as a private fallback.
  • Pre-fetch and re-host in your fork's docs/ directory if you maintain a fork. This grows the fork's repo size by ~100 MB cumulative across the archive but eliminates external image dependence.
  • Use web.archive.org snapshots of issue pages to reconstruct images post-hoc if the CDN ever fails permanently.

Caution

Forks should not silently mirror cdn.beekka.com to their own CDN and present the images as their own. The CDN is author-property. Republishing the images either requires permission or must be clearly attributed. The simpler path is to keep hot-linking from upstream's CDN and accept the tail-risk of CDN failure.

If the goal is to be a robust mirror against upstream-disappears scenarios, the recommended pattern is:

  1. Fork the repository on GitHub. Your fork is a complete Markdown backup.
  2. Run a daily git fetch upstream master && git push origin mirror workflow on a personal machine or GitHub Actions runner, keeping your fork's mirror branch synchronized.
  3. Periodically scrape the image CDNs (cdn.beekka.com, www.ruanyifeng.com/blogimg/) for every URL referenced in the Markdown, into a personal archive bucket. This requires no permission as it is your private archive, not a public re-host.
  4. If upstream ever disappears, you have content (your fork) and visuals (your image archive) and can re-publish under fair-use norms or with prior author permission.

This costs roughly $0–$5/month on consumer cloud storage and yields a durable backup of the eight-year archive.

7.5 Secrets

There are no secrets committed to this repository. No API keys, no tokens, no credentials. The author's blog runs secrets in its own infrastructure (database password, mail server credentials), not here.

If you fork and add CI workflows (e.g. the dead-link checker proposed above, an auto-deploy to your own CDN, or a translation pipeline), use GitHub Actions secrets rather than committed config files. Audit .gitignore regularly for any local secret file you may have accumulated.

7.6 Author contact for security disclosures

If you find something that looks security-relevant — a hijacked outbound link, a compromised CDN path, a phishing payload in an issue-tracker comment — email [email removed] rather than filing a public issue. Include the issue number, the URL, and a description of the malicious behavior. For routine dead links and minor factual errors, the public tracker is appropriate.


8. Developer Internals

This section is for developers building tooling around ruanyf/weekly. There are no internals in the application sense, so what follows are conventions worth scripting against, gaps worth filling, and patterns worth replicating in derivative newsletters.

8.1 Repository internals at a glance

weekly/
├── README.md                Reverse-chronological master index, hand-maintained
├── .gitignore               Excludes local Node/Yarn tooling (3 entries)
└── docs/
    ├── issue-1.md           Issue 1 — 2018-04-23 (oldest)
    ├── issue-2.md
    ├── ...                  393 sequentially-numbered files, no gaps
    ├── issue-393.md         Current issue — 2026-04-17 (newest)
    ├── free-music.md        Royalty-free music libraries
    ├── free-photos.md       Free stock-photo libraries
    ├── free-software.md     Free creative tools
    └── subjects/
        ├── css.md
        ├── database.md
        ├── dev.md
        ├── docker.md
        ├── go.md
        ├── linux.md
        ├── math.md
        └── web.md

No .github/ directory. No LICENSE. No CONTRIBUTING.md. No build artifacts. No vendored dependencies. No package.json (it is gitignored).

8.2 Working with the issue files programmatically

A reliable parser for an issue file:

#!/usr/bin/env python3
"""Parse a ruanyf/weekly issue file into structured fields."""
import re
from pathlib import Path
from dataclasses import dataclass, field
from typing import List

ISSUE_TITLE = re.compile(
    r"^#\s*(?P<magazine>科技爱好者周刊|每周分享)"
    r"(?:[((]\s*第\s*(?P<num>\d+)\s*期\s*[))])?"
    r"(?:[::]\s*(?P<title>.+))?\s*$",
)

@dataclass
class Issue:
    number: int
    magazine: str
    title: str
    sections: List[str] = field(default_factory=list)

def parse(path: Path) -> Issue:
    lines = path.read_text(encoding="utf-8").splitlines()
    m = ISSUE_TITLE.match(lines[0])
    if not m:
        raise ValueError(f"H1 mismatch in {path}")
    sections = [
        line[3:].strip()
        for line in lines
        if line.startswith("## ")
    ]
    return Issue(
        number=int(m.group("num") or path.stem.split("-")[1]),
        magazine=m.group("magazine"),
        title=(m.group("title") or "").strip(),
        sections=sections,
    )

if __name__ == "__main__":
    for p in sorted(Path("docs").glob("issue-*.md")):
        issue = parse(p)
        print(f"{issue.number:>3}  {issue.magazine}  {issue.title}")

Notes:

  • The magazine field captures whether the file uses the original 每周分享 or the renamed 科技爱好者周刊. Roughly the first 30 issues use the original.
  • The H1 regex tolerates both ASCII and full-width parentheses for backward compatibility, even though current issues use full-width exclusively.
  • Section names are returned in document order — useful for building section-presence statistics across the archive.

8.3 Working with the README index programmatically

The index is hand-formatted Markdown. A defensive parser walks year and month headings and extracts issue bullets:

#!/usr/bin/env python3
"""Parse README.md into (year, month, issue_number, title, path) tuples."""
import re
from pathlib import Path

YEAR_HEADING = re.compile(r"^##\s+(\d{4})\s*$")
MONTH_HEADING = re.compile(r"^\*\*(.+?)\*\*\s*$")
ISSUE_BULLET = re.compile(
    r"^-\s*第\s*(\d+)\s*期\s*[::]\s*\[(.+?)\]\(docs/issue-\1\.md\)\s*$"
)

def parse_index(readme: Path):
    year, month = None, None
    for line in readme.read_text(encoding="utf-8").splitlines():
        if (m := YEAR_HEADING.match(line)):
            year = int(m.group(1))
        elif (m := MONTH_HEADING.match(line)) and year:
            month = m.group(1).strip()
        elif (m := ISSUE_BULLET.match(line)) and year and month:
            yield year, month, int(m.group(1)), m.group(2)

Use cases: index regeneration, gap detection, year-by-year statistics, building a structured metadata sidecar (none exist upstream).

8.4 Detecting issue-number gaps

One five-line sanity check confirms the sequence is unbroken:

ls docs/issue-*.md | sed 's/[^0-9]//g' | sort -n | \
  awk 'NR>1 && $1 != prev+1 { printf "gap: missing issue %d\n", prev+1 }; { prev = $1 }'
(no output — no gaps in 1..393 at the source commit)

A wrapper for CI:

#!/usr/bin/env bash
# .github/workflows/check-no-gaps.sh
set -euo pipefail
gaps=$(ls docs/issue-*.md | sed 's/[^0-9]//g' | sort -n | \
       awk 'NR>1 && $1 != prev+1 { printf "%d ", prev+1 }; { prev = $1 }')
if [ -n "$gaps" ]; then
    echo "Missing issue numbers: $gaps" >&2
    exit 1
fi
echo "Issue sequence is unbroken (1..$prev)"

8.5 Detecting schema drift

If you want to enforce that every issue file conforms to the standard schema (boilerplate paragraph after H1, presence of (完) closer, recognized section vocabulary), a stricter validator:

#!/usr/bin/env python3
"""Schema validator for weekly issue files."""
from pathlib import Path

RECOGNIZED_SECTIONS = {
    "封面图", "科技动态", "AI 相关", "工具", "资源",
    "图片", "文摘", "言论", "往年回顾", "文档", "电子书",
}

def validate(path: Path) -> list[str]:
    text = path.read_text(encoding="utf-8")
    errors: list[str] = []
    if not text.startswith("# "):
        errors.append("missing H1")
    if "(完)" not in text and int(path.stem.split("-")[1]) >= 53:
        errors.append("missing (完) closer (expected from issue 53 onward)")
    for raw_h2 in (line[3:].strip() for line in text.splitlines() if line.startswith("## ")):
        canonical = raw_h2.split(":")[0].strip()
        if canonical not in RECOGNIZED_SECTIONS and not canonical.startswith("第") :
            # Free-form essay headings are allowed; flag others
            pass  # too permissive to fail in CI; warn instead
    return errors

if __name__ == "__main__":
    for p in sorted(Path("docs").glob("issue-*.md")):
        for err in validate(p):
            print(f"{p}: {err}")

Warnings rather than errors for unrecognized H2s — opinion-essay headings are free-form and should not fail validation.

8.6 Suggested improvements (none committed upstream)

The repository has run for eight years without any of this tooling. Each item is a clean addition that a fork or downstream could invest in:

# Improvement Effort Value
1 Automated README.md index regeneration Low Eliminates the only common authoring slip (forgotten index bump)
2 Nightly dead-link checker (lychee in GitHub Actions) Low Surfaces link rot before readers report it
3 Issue-number-gap detector in CI Trivial Catches a never-yet-occurring failure mode
4 Structured submission templates (.github/ISSUE_TEMPLATE/*.yml) Low Reduces triage time; nudges submitters to include required fields
5 Explicit LICENSE (e.g. CC-BY-NC-ND for prose) Low Resolves the implicit-copyright ambiguity for translators and mirrors
6 Topic-page refresh sweep (promote evergreen 工具 entries into docs/subjects/*) Medium Turns the topic directory into a genuine reference
7 Submission-acknowledgement bot (auto-comment "thanks, queued for review") Low Sets submitter expectations without adding work
8 Translation channel formalization (docs/{lang}/issue-{N}.md) Medium Captures community translation effort durably
9 Schema validator in CI (warns on unrecognized H2s, missing (完)) Medium Prevents subtle drift from canonical schema
10 Image-mirror automation (sync cdn.beekka.com URLs to a secondary host) Medium Eliminates the CDN single-point-of-failure

8.7 Code patterns and conventions to preserve in derivatives

If you fork the repository to start your own weekly newsletter, the patterns worth preserving:

  • One Markdown file per issue, sequentially numbered, no leading zeros. Predictable filenames, predictable URLs, easy programmatic access.
  • No front-matter. Markdown stays readable on raw GitHub, avoids static-site-generator lock-in, plays well with any renderer.
  • External CDN for images, never committed binaries. Keeps the Git history small, avoids LFS, lets you re-host or re-encode without rewriting Git.
  • Issue tracker as submission inbox. Free, public, comment-able, scales effortlessly.
  • Reverse-chronological hand-maintained index. Until you have hundreds of issues, automation is overkill; the discipline of weekly index-bumps catches mistakes.
  • Cross-post to a self-hosted blog with a stable URL pattern. Citation stability is important for academic and journalistic uses.
  • No staging environment. The editorial gate is the human, not a workflow. Land typo fixes as follow-up commits.

8.8 Testing

There is no test suite. There is nothing to test in a content-only repository — Markdown either renders or it does not, and rendering is GitHub's responsibility. Validation, when it exists in a fork, is at the schema layer (issue-number sequence, section vocabulary, image URL format), not at the application layer.

8.9 Contributing

Contribution routes for upstream:

  1. Submit a tool, website, or open-source project — open an issue with 【工具自荐】, 【网站自荐】, or 【开源自荐】 prefix.
  2. Report a correction (typo, broken link, factual error) — open an issue titled 第 N 期更正 or 第 N 期链接失效.
  3. Comment on the Who's Hiring thread (#9454) — for hiring or job-hunting.
  4. Open a pull request — rare but accepted for clear-cut fixes (typo correction in an existing issue, README index repair). The author generally prefers a well-formed issue to a PR because issues are faster to triage; for a fix that touches multiple typos in one file, a PR is efficient.

What not to PR:

  • Essay additions or rewrites — essays are the author's personal voice.
  • Reordering of the README index — reverse-chronological order is intentional.
  • License changes — only the author can decide on a license.
  • Automation or CI additions without prior discussion — the upstream project keeps infrastructure minimal by design.

9. Integration Points

9.1 GitHub integration

The repository integrates with GitHub primitives in straightforward ways:

Primitive Use
Repository Source of truth for Markdown content
Issues Submission inbox, bug tracker, Who's Hiring board
Pinned issue Active job board (#9454)
Stars / watches Reader signal; no operational role
Forks Personal archives, translations, derivative newsletters
Pull requests Rare; accepted for typo fixes
Search Full-text repo search for readers
Notifications Bell icon on pinned issue can be muted by readers
GitHub API None used by upstream; available to fork tooling

There is no GitHub App, no Webhook, no Action, no Bot. Every operation is performed by the author through the GitHub web UI or git CLI directly.

9.2 Blog integration

The blog at ruanyifeng.com is manually integrated:

  • Each weekly issue is cross-posted by hand from Markdown to the blog's HTML rendering pipeline.
  • The blog's URL pattern /blog/{YYYY}/{MM}/weekly-issue-{N}.html is stable and citation-friendly.
  • The blog's RSS feed at /blog/atom.xml exposes the new issue to subscribers.
  • The blog's per-post comment thread carries reader discussion that does not mirror back to GitHub.

There is no automation between the GitHub repository and the blog. A fork that wants automated cross-posting must build its own pipeline (watch docs/issue-*.md, render to the target blog's template, POST to its publishing API). Upstream does not ship such a pipeline because the author runs his blog stack privately.

9.3 RSS integration

The RSS feed is the primary subscription channel. It is fed by the blog, not by the GitHub repository. A new weekly issue appears in the feed within minutes of being cross-posted to the blog. Reader integrations:

  • Direct subscription via any RSS reader (Feedly, Inoreader, NetNewsWire, NewsBlur, Reeder, Tiny Tiny RSS, Miniflux).
  • RSS-to-email gateways (Blogtrottr, Kill the Newsletter, Feed-to-Email) for readers who want email delivery.
  • IFTTT / Zapier integrations for routing new issues to read-it-later apps, Slack channels, Discord servers, Telegram bots, etc.

9.4 Image-CDN integration

Images integrate with two upstream hosts:

  • cdn.beekka.com/blogimg/asset/{YYYYMM}/{filename} — modern uploads.
  • www.ruanyifeng.com/blogimg/asset/{YYYY}/{filename} — legacy uploads.

Both are HTTPS-only. There is no auth-walled access, no signed URL, no token. Hot-linking works without coordination.

9.5 Static-site-generator integration

The repository is compatible with most modern Markdown-to-HTML pipelines:

  • MkDocs (Material theme) — works out of the box; minimal mkdocs.yml provided in the User Guide.
  • Hugo — works if frontmatter: false is set for the docs/ archetype.
  • Docusaurus — works with a sidebars.js that explicitly enumerates the issue files.
  • Jekyll — works if you accept that the issue files are "pages" rather than "posts" (no front-matter dates).
  • Pandoc — works for one-off conversions (Markdown → HTML, EPUB, PDF, DOCX).

CJK font support is the only common pitfall when generating PDFs — install fonts-noto-cjk (Debian/Ubuntu) or an equivalent font package on the build host, or use a renderer that bundles CJK fallbacks (modern Chrome print-to-PDF, Typora export).

9.6 LLM and AI-tooling integration

The full archive at ~3 MB / ~1.5 M tokens fits comfortably in a single large-context LLM call (Claude 200K+, GPT-4 Turbo 128K, Gemini Pro 2M). Common integration patterns:

  • Embedding the archive for semantic search. ~$0.03 cost using OpenAI text-embedding-3-small.
  • Summarization of an issue or a date range. Single-shot prompt with the issue file as context.
  • Translation pipelines that produce an English mirror per issue. Each issue is small (~3.8 K tokens), so per-issue translation is fast and cheap.
  • Topic extraction by passing the entire archive plus a "list every issue mentioning X" instruction.

Forks that operate AI tooling against the corpus should:

  1. Respect copyright — the prose is the author's. Generated derivatives should attribute and link the source issue.
  2. Cache embeddings, do not regenerate. The corpus changes by one new issue per week; recompute is wasteful.
  3. Strip the boilerplate paragraph and the (完) closer from the indexable surface — they recur identically in every issue and pollute embedding space.

9.7 Backup and archival integration

Target Mechanism Cadence
web.archive.org snapshots Manual or via Save Page Now API Per issue, opportunistically
Personal Git mirror git clone + git fetch cron Daily
Personal image archive wget against extracted CDN URLs Quarterly
Personal blog scrape wget -m -p against /blog/ Quarterly

None of these are upstream operations — they are belt-and-braces backup recommendations for fork-owners or institutional mirrors.