编程 从零构建 Redis、Docker、Git:build-your-own-x 49 万星的背后——真正掌握技术底层逻辑的终极学习法(2026 深度实战)

2026-05-29 05:07:59 +0800 CST views 8

从零构建 Redis、Docker、Git:build-your-own-x 49 万星的背后——真正掌握技术底层逻辑的终极学习法(2026 深度实战)

你用 Redis 用了三年,能说出它的事件循环是怎么写的吗?你每天 docker run,知道 namespace 和 cgroup 在内核里是怎么编排的吗?这篇文带你用「从零构建」的方式,把那些你"会用但说不清"的技术,真正学透。


一、前言:为什么我们学了那么多,还是写不出"有灵魂"的代码?

我是程序员茄子。

过去一年,我面试了不下 50 个后端候选人。一个很有意思的现象:几乎所有人都说得出 Redis 是单线程的,却没人能解释 epoll 为什么比 select 快两个数量级;几乎所有人都在用 Docker 部署服务,却没人能说清容器和虚拟机的本质区别到底在哪一层

这不是候选人的问题,这是学习方式的问题

我们这代程序员,是在"框架时代"长大的。Spring Boot 一键启动,Docker 一键部署,Redis 一键缓存。我们用的是"封装好的轮子",却很少有机会去看看轮子里面长什么样。

你可能会说:我只要能解决问题就行了,为什么要去造轮子?

这个问题问得好。答案也很直接:

不造轮子,你永远只能做一个"API 调用工程师",而不是一个"系统设计师"。

今天要讲的这个项目——build-your-own-x,在 GitHub 上拿了 49 万 Star,每天还在以 170+ Star 的速度增长。它不做广告,不蹭热点,只是安静地收集了一份"从零构建各种技术"的学习资源列表。

但正是这份列表,击中了无数程序员内心最深处的那个痛点:我想真正搞懂它,而不是只会用

本文将带您深入 build-your-own-x 的学习方法论,并以 从零实现 Redis 核心、从零实现 Docker 容器、从零实现 Git 三个实战案例,手把手教您如何用「造轮子」的方式真正掌握技术的底层逻辑。全文约 12000 字,建议收藏慢读。


二、build-your-own-x 是什么?为什么它能拿 49 万 Star?

2.1 项目概况

  • 项目地址:https://github.com/codecrafters-io/build-your-own-x
  • Star 数:490,000+(GitHub 历史前 20)
  • 日均新增 Star:170+(持续 5 年不减)
  • 核心内容:收集了从零实现各种技术的教程、论文、代码仓库链接,涵盖数据库、操作系统、编译器、网络、分布式系统等方向。

这个项目不是一本"书",而是一份精心策划的地图——它告诉你:如果你想从零构建一个 Redis,有哪些最好的教程可以参考;如果你想自己写一个 Docker,有哪些开源实现可以学习。

2.2 为什么这份"地图"值 49 万 Star?

因为它解决了一个真实存在的需求:高质量的「系统级深度学习者」找不到好的学习路径

市面上的技术教程分两类:

类型代表问题
快速入门型"30 分钟学会 Redis"只教 API,不教原理
学术理论型数据库系统概论(课本)理论完备,但离代码太远

build-your-own-x 填补的空白是:它推荐的每一篇教程,都是「理论 + 代码」双驱动的——你一边看论文,一边在写代码,写完之后你不仅"知道"了,而且"做出来"了。

这种学习方式的认知深度,是纯粹「阅读」无法比拟的。

认知科学告诉我们:主动建构的知识, retention(保留率)是被动阅读的 3-5 倍。


三、核心方法论:为什么要「从零构建」?

3.1 阅读的悖论

你有没有过这种体验:

读完一本《Redis 设计与实现》,感觉自己懂了。过两周,有人问你"Redis 的渐进式 rehash 是怎么工作的?",你脑子一片空白。

这不是你记忆力不好,这是被动阅读的天然缺陷——你以为你"理解"了,其实你只是"认得"那些字。

真正的理解,只有在你亲手实现一遍之后才会发生。

3.2 从零构建的四个认知层次

以 Redis 为例,看看「从零构建」如何把认知推到四个层次:

层次方式你能回答的问题
L1:使用者SET key valueRedis 支持哪些数据结构?
L2:原理理解者读过《Redis 设计与实现》Redis 为什么单线程还这么快?
L3:实现者自己写过简化版 Redis事件循环里 epollaccept 是怎么协作的?
L4:设计者在简化版上做过性能优化为什么 Redis 6.0 引入了多线程 IO?

build-your-own-x 的核心价值,就是帮你从 L1/L2 跨到 L3/L4。

3.3 选一个切入点:Redis / Docker / Git

为什么要选这三个?因为它们分别代表三种不同类型的系统:

  • Redis:高性能网络服务 + 数据结构引擎(网络编程 + 算法)
  • Docker:操作系统级虚拟化(内核机制 + 系统编程)
  • Git:分布式版本控制(数据结构 + 分布式系统基础)

把这三个做一遍,你对「系统编程」的理解会上升一个维度。


四、实战一:从零实现一个简化版 Redis(deep-redis)

目标:用 ~2000 行 C 代码,实现一个支持 SET/GET/DEL/EXPIRE 的简化版 Redis,理解事件循环、内存管理、过期策略的底层实现。

4.1 架构设计

一个最小可用 Redis(我们叫它 deep-redis)的核心组件:

deep-redis/
├── src/
│   ├── server.c          # 主事件循环(epoll/kqueue)
│   ├── conn.c            # 连接管理(accept + 协议解析)
│   ├── dict.c            # 哈希表(动态扩缩容 + rehash)
│   ├── expire.c          # 过期策略(惰性删除 + 定期删除)
│   ├── aof.c            # AOF 持久化(append-only log)
│   └── util.c            # 内存分配器封装
├── tests/
│   └── test_basic.c     # 基础功能测试
└── Makefile

4.2 第一步:事件循环——Redis 性能的命门

Redis 的单线程高性能,核心秘密在 事件循环。它不是简单的 while(true) + read(),而是基于 I/O 多路复用epoll(Linux)或 kqueue(macOS)。

// deep-redis/src/server.c(核心事件循环简化版)

#include <sys/epoll.h>
#include <unistd.h>
#include "conn.h"

#define MAX_EVENTS 1024

void event_loop(int server_fd) {
    int epfd = epoll_create1(0);
    struct epoll_event ev, events[MAX_EVENTS];
    
    // 把 server_fd 注册到 epoll
    ev.events = EPOLLIN;
    ev.data.fd = server_fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &ev);
    
    while (1) {
        int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == server_fd) {
                // 新连接到来
                accept_new_client(server_fd, epfd);
            } else {
                // 已连接客户端有数据可读
                handle_client_request(events[i].data.fd, epfd);
            }
        }
    }
}

关键点解析

  1. epoll_wait 阻塞等待:与 select 不同,epoll 用红黑树管理 fd,每次只返回「有事件的 fd」,时间复杂度 O(1) 而非 O(n)。
  2. 边缘触发(ET)vs 水平触发(LT):Redis 使用 LT 模式,更简单,不容易丢事件。
  3. 为什么单线程能处理万级并发? 因为 epoll 让一个线程可以同时"监听"数万个连接,只在有真正 I/O 事件时才处理,没有空转。

4.3 第二步:哈希表——Redis 字典的心脏

Redis 的 dict 是实现 SET/GET 的底层数据结构。它的精妙之处在于 渐进式 rehash——在扩容时,不是一次性把老表的数据搬完(会阻塞),而是每次操作搬一小部分。

// deep-redis/src/dict.c(简化版)

typedef struct dictEntry {
    char *key;
    char *value;
    struct dictEntry *next;  // 链地址法解决冲突
} dictEntry;

typedef struct dict {
    dictEntry **table;      // 哈希表数组
    long size;               // 表大小(2 的幂)
    long used;               // 已使用槽位数
    long rehashidx;          // rehash 进度,-1 表示未进行
    dictEntry **ht[2];       // 两个表:ht[0] 旧,ht[1] 新
} dict;

// 渐进式 rehash:每次操作搬 100 个桶
void dictRehash(dict *d, int n) {
    if (d->rehashidx == -1) return;
    
    while (n--) {
        dictEntry *de;
        while ((de = d->ht[0][d->rehashidx]) == NULL) {
            d->rehashidx++;
            if (d->rehashidx >= d->ht[0]->size) {
                // 搬完了,切换表
                zfree(d->ht[0]);
                d->ht[0] = d->ht[1];
                d->ht[1] = NULL;
                d->rehashidx = -1;
                return;
            }
        }
        // 把 ht[0][rehashidx] 链表搬到 ht[1]
        // ...(省略具体搬迁代码)
        d->ht[0][d->rehashidx] = NULL;
        d->rehashidx++;
    }
}

为什么这样做?

假设哈希表有 100 万个 key,一次性 rehash 需要几十毫秒——对 Redis 这种毫秒级响应的系统来说,这是灾难。渐进式 rehash 把这个耗时打散到每次操作里,保证了低延迟。

4.4 第三步:过期策略——你不知道的两种删除

Redis 的过期删除不是"到期立即删除",而是两种策略组合:

  1. 惰性删除:访问 key 时检查是否过期,过期则删除(保证访问时数据正确)
  2. 定期删除:每隔一段时间(默认 10 次/秒),随机抽查一批 key,删除过期的(防止内存泄漏)
// deep-redis/src/expire.c

// 惰性删除:在 GET 时检查
char *getCommand(dict *d, const char *key) {
    dictEntry *e = dictFind(d, key);
    if (!e) return NULL;
    
    if (isExpired(e)) {
        dictDelete(d, key);  // 惰性删除
        return NULL;
    }
    return e->value;
}

// 定期删除:每秒执行 10 次
void activeExpireCycle(void) {
    int dbs_per_call = 16;
    for (int i = 0; i < dbs_per_call; i++) {
        // 每次随机抽查 20 个 key
        int expired = 0;
        for (int j = 0; j < 20; j++) {
            char *key = randomKeyFromDB(i);
            if (key && isExpiredByKey(key)) {
                dictDelete(server.db[i], key);
                expired++;
            }
        }
        // 如果这一轮删除了超过 5 个(25%),继续删
        if (expired > 5) continue;
        else break;
    }
}

这个设计的精妙之处:如果只靠惰性删除,过期 key 永远不被访问就会一直占内存(内存泄漏);如果只靠定期删除,过期 key 太多时删除会阻塞。两者结合,在保证性能的前提下最大化内存回收效率。

4.5 小结:你学到了什么?

当你亲手实现完这个简化版 Redis,以下问题是你真正能回答的:

  • ✅ 为什么 Redis 用单线程?单线程怎么做到高并发?
  • ✅ 渐进式 rehash 是怎么工作的?为什么不直接一次性 rehash?
  • ✅ 过期 key 是怎么被删除的?为什么不用定时器和立即删除?
  • ✅ AOF 持久化和 RDB 快照的本质区别是什么?

这些不是"背"来的,是"写"来的。


五、实战二:从零实现一个简化版 Docker(deep-container)

目标:用 ~1500 行 C 代码 + Linux 系统调用,实现一个支持 deeprun image command 的简化容器运行时,理解 namespace、cgroup、overlayfs 的底层机制。

5.1 Docker 的本质:它不是"虚拟机"

这是最常见的误解。

对比维度虚拟机Docker 容器
隔离级别硬件级(Hypervisor)进程级(namespace)
内核每个 VM 有独立内核所有容器共享宿主机内核
启动速度分钟级秒级/毫秒级
资源开销高(每个 VM 跑完整 OS)低(只是普通进程)

Docker 的本质:利用 Linux 内核的两个能力——namespace(隔离)和 cgroup(资源限制),让一个进程"以为"自己在一台独立的机器上运行。

5.2 第一步:用 namespace 实现隔离

Linux 有 7 种 namespace,Docker 用了其中 6 个:

// deep-container/src/namespace.c

#define _GNU_SOURCE
#include <sched.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/mount.h>

// 创建容器进程(在 namespace 隔离环境中)
int container_main(void *arg) {
    // 此时已经在新的 namespace 里了
    
    // 1. 挂载新的 rootfs(用 pivot_root 或 chroot)
    mount_overlayfs();
    
    // 2. 在容器内执行命令
    char *const argv[] = { "/bin/bash", NULL };
    execvp(argv[0], argv);
    
    // execvp 成功后不会返回
    perror("execvp failed");
    return 1;
}

int main(int argc, char *argv[]) {
    char stack[1024 * 1024];  // 子进程栈
    
    // 创建子进程,并加入新的 namespace
    // CLONE_NEWPID: 新的 PID namespace(容器内 PID 1)
    // CLONE_NEWNET: 新的网络 namespace(独立网络栈)
    // CLONE_NEWNS:  新的 mount namespace(独立挂载点)
    // CLONE_NEWUTS: 新的 hostname namespace
    pid_t pid = clone(container_main, stack + sizeof(stack),
                      CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS |
                      CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD,
                      NULL);
    
    waitpid(pid, NULL, 0);
    return 0;
}

关键点

  1. clone() 系统调用:类似于 fork(),但可以指定哪些 namespace 要新建。这是容器隔离的入口。
  2. PID namespace:容器内的进程看不到宿主机的进程,且容器内 init 进程的 PID 是 1(像真正的 OS 一样)。
  3. network namespace:每个容器有独立的网络栈(独立 IP、端口空间),所以用 Docker 跑多个 Nginx 不会端口冲突。

5.3 第二步:用 cgroup 限制资源

namespace 解决了"隔离"问题,但不解决"资源抢占"问题——一个容器跑了死循环,可以把宿主机 CPU 吃光。

cgroup(control group) 用来限制容器能用的资源上限:

// deep-container/src/cgroup.c

// 把进程 pid 加入 cgroup,限制其 CPU 和内存
void setup_cgroup(pid_t pid, int cpu_quota, long mem_limit) {
    char path[256];
    
    // 1. 创建 cgroup(在 /sys/fs/cgroup/ 下)
    mkdir("/sys/fs/cgroup/deep-container", 0755);
    
    // 2. 设置 CPU 配额(单位是微秒,100000 = 10% 的 CPU)
    sprintf(path, "/sys/fs/cgroup/deep-container/cpu.max");
    FILE *fp = fopen(path, "w");
    fprintf(fp, "%d 100000\n", cpu_quota);  // 例如 50000 = 50% CPU
    fclose(fp);
    
    // 3. 设置内存上限(单位字节)
    sprintf(path, "/sys/fs/cgroup/deep-container/memory.max");
    fp = fopen(path, "w");
    fprintf(fp, "%ld\n", mem_limit);  // 例如 536870912 = 512MB
    fclose(fp);
    
    // 4. 把容器进程加入 cgroup
    sprintf(path, "/sys/fs/cgroup/deep-container/cgroup.procs");
    fp = fopen(path, "w");
    fprintf(fp, "%d\n", pid);
    fclose(fp);
}

cgroup v2 的核心文件(在 /sys/fs/cgroup/ 下):

文件作用
cpu.maxCPU 配额(微秒/100000)
memory.max内存上限(字节)
pids.max最大进程数(防 fork 炸弹)
cgroup.procs把进程加入此 cgroup

5.4 第三步:用 overlayfs 实现镜像分层

Docker 镜像的分层设计是它的精髓——你拉取的 Ubuntu 镜像 500MB,但如果你已经在本地有了 Ubuntu 基础层,只需要拉差异层。

overlayfs 是 Linux 的联合文件系统,可以把多个目录"叠加"成一个目录:

overlayfs 挂载结构:
lowerdir=/var/lib/deep-container/layers/ubuntu    (只读基础层)
upperdir=/var/lib/deep-container/overlay/abc123   (可写层,容器专属)
workdir=/var/lib/deep-container/work/abc123       (内部工作目录)
merged=/var/lib/deep-container/rootfs/abc123      (最终挂载点,容器内看到的根目录)

挂载命令:
mount -t overlay overlay \
  -o lowerdir=/layers/ubuntu,upperdir=/overlay/abc123,workdir=/work/abc123 \
  /rootfs/abc123

在代码中实现

// deep-container/src/overlayfs.c

void mount_overlayfs(const char *container_id) {
    char lowerdir[512], upperdir[512], workdir[512], merged[512];
    char options[1024];
    
    sprintf(lowerdir, "/var/lib/deep-container/layers/ubuntu");
    sprintf(upperdir, "/var/lib/deep-container/overlay/%s", container_id);
    sprintf(workdir, "/var/lib/deep-container/work/%s", container_id);
    sprintf(merged, "/var/lib/deep-container/rootfs/%s", container_id);
    
    mkdir(upperdir, 0755);
    mkdir(workdir, 0755);
    mkdir(merged, 0755);
    
    sprintf(options, "lowerdir=%s,upperdir=%s,workdir=%s",
            lowerdir, upperdir, workdir);
    
    // 执行 mount 系统调用
    if (mount("overlay", merged, "overlay", 0, options) != 0) {
        perror("overlayfs mount failed");
    }
    
    // 切换到新的 rootfs(pivot_root)
    pivot_root(merged, merged);
}

5.5 小结

实现完 deep-container,你真正理解了:

  • ✅ 容器和虚拟机的本质区别在哪一层?
  • ✅ namespace 的 6 种类型分别隔离了什么?
  • ✅ cgroup 是怎么限制 CPU/内存的?为什么 Docker 不会把宿主机资源吃光?
  • ✅ overlayfs 是怎么实现镜像分层的?为什么 Docker 镜像拉取这么快?

六、实战三:从零实现一个简化版 Git(deep-git)

目标:用 ~1200 行 C 代码,实现一个支持 init/add/commit/log 的简化版 Git,理解对象模型、Merkle DAG、引用管理的底层原理。

6.1 Git 的对象模型:一切皆对象

Git 底层只有 四种对象,所有功能都建立在这四种对象之上:

blob    —— 文件内容(不含文件名)
tree    —— 目录结构(指向 blob 和其他 tree)
commit  —— 一次提交(指向一个 tree,含作者/时间/消息)
tag     —— 标签(指向一个 commit)

每种对象都有一个 SHA-1 哈希值(现在也支持 SHA-256),作为对象的唯一 ID。这就是 Git 的「内容寻址」——你不需要"文件名"来找到一个文件,你用它的哈希值。

// deep-git/src/object.c

#include <openssl/sha.h>

// 计算对象的 SHA-1 哈希
char *hash_object(const char *type, const char *content, size_t len) {
    SHA_CTX ctx;
    unsigned char hash[SHA_DIGEST_LENGTH];
    char *hex = malloc(SHA_DIGEST_LENGTH * 2 + 1);
    
    // Git 对象格式:"{type} {len}\0{content}"
    char *header;
    asprintf(&header, "%s %zu", type, len);
    
    SHA1_Init(&ctx);
    SHA1_Update(&ctx, header, strlen(header) + 1);  // +1 包含 \0
    SHA1_Update(&ctx, content, len);
    SHA1_Final(hash, &ctx);
    
    // 转成十六进制
    for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
        sprintf(hex + i * 2, "%02x", hash[i]);
    }
    return hex;  // 返回 40 字符的 hex 字符串
}

6.2 blob 和 tree:Git 怎么存储文件?

当你 git add 一个文件,Git 做了两件事:

  1. 把文件内容存成一个 blob 对象(用 zlib 压缩)
  2. 更新 index(暂存区),记录文件名 → blob 哈希的映射
// deep-git/src/blob.c

// 把文件内容存储为 blob 对象
void write_blob(const char *filepath) {
    // 1. 读取文件内容
    FILE *fp = fopen(filepath, "rb");
    fseek(fp, 0, SEEK_END);
    long len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    char *content = malloc(len);
    fread(content, 1, len, fp);
    fclose(fp);
    
    // 2. 计算 SHA-1
    char *hash = hash_object("blob", content, len);
    
    // 3. 用 zlib 压缩并写入 .git/objects/
    char obj_path[512];
    sprintf(obj_path, ".git/objects/%.2s/%s", hash, hash + 2);
    mkdir_p(obj_path);  // 创建目录(前2位作为子目录名)
    
    gzFile gz = gzopen(obj_path, "wb");
    gzwrite(gz, content, len);
    gzclose(gz);
    
    free(content);
}

tree 对象 储存目录结构,它本质上是一个「指向 blob 和其他 tree 的指针列表」:

// tree 对象格式(文本表示):
// 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c539    README.md
// 100755 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c539    run.sh
// 040000 tree e69de29bb2d1d6434b8b29ae775ad8c2e48c539    src/

6.3 commit 对象:把所有东西串起来

一个 commit 对象 指向一个 tree(那一刻的整个项目快照),并形成一条链:

commit A (tree_A) → parent: none   (初始提交)
commit B (tree_B) → parent: A
commit C (tree_C) → parent: B
// deep-git/src/commit.c

void write_commit(const char *tree_hash, const char *parent_hash,
                  const char *author, const char *message) {
    char content[4096];
    char *now = get_current_timestamp();
    
    // commit 对象格式
    sprintf(content,
        "tree %s\n"
        "parent %s\n"    // 初始提交没有 parent
        "author %s %s\n"
        "committer %s %s\n"
        "\n"
        "%s\n",
        tree_hash,
        parent_hash ? parent_hash : "",
        author, now,
        author, now,
        message
    );
    
    char *commit_hash = hash_object("commit", content, strlen(content));
    
    // 写入 .git/objects/
    char obj_path[512];
    sprintf(obj_path, ".git/objects/%.2s/%s", commit_hash, commit_hash + 2);
    write_compressed(obj_path, content, strlen(content));
    
    // 更新 HEAD(当前分支指向这个新 commit)
    update_ref("HEAD", commit_hash);
}

6.4 为什么 Git 这么快?Merkle DAG 的威力

Git 的版本比较为什么这么快?因为它用的是 Merkle DAG(有向无环图):

  • 每个 commit 的哈希值依赖于它的 parent,形成一条不可篡改的链
  • 比较两个版本,只需要比较根 tree 的哈希值——如果一样,整个项目完全一样;如果不一样,递归比较子 tree/blob
  • 这意味着比较任意两个版本的时间复杂度是 O(变更文件数),而不是 O(项目总文件数)

6.5 小结

实现完 deep-git,以下问题你有答案了:

  • ✅ Git 的「内容寻址」是什么意思?为什么用 SHA-1 哈希?
  • ✅ blob/tree/commit 三个对象是怎么组织的?
  • git checkout 为什么这么快?(只需要把目标 commit 的 tree 恢复到工作区)
  • ✅ Git 的分布式本质:为什么每个 clone 都是完整备份?

七、从 build-your-own-x 到其他系统:学习路径推荐

完成了 Redis/Docker/Git 三个实战,你已经具备了系统编程的核心能力。接下来可以按这个路径继续深入:

7.1 数据库方向

项目推荐教程核心收获
从零实现 SQLite《Database Internals》+ cstack/db_tutorialB-tree、WAL、事务 ACID
从零实现分布式 KVetcd 源码 + Raft 论文分布式共识、Raft 协议
从零实现列式存储ClickHouse 源码向量化执行、压缩编码

7.2 网络方向

项目推荐教程核心收获
从零实现 HTTP 服务器tinyhttpd 源码HTTP 协议、TCP 连接管理
从零实现 TCP 协议栈lwIP 源码三次握手、滑动窗口、拥塞控制
从零实现 RPC 框架跟着 gRPC 源码读Protobuf 编码、HTTP/2 多路复用

7.3 编译器方向

项目推荐教程核心收获
从零实现解释器《Crafting Interpreters》词法分析、语法分析、AST、字节码 VM
从零实现 JIT 编译器luajit 源码动态编译、寄存器分配

八、写在最后:49 万 Star 的真正启示

build-your-own-x 的流行,反映了一件事:

程序员群体正在从「工具使用者」向「系统理解者」觉醒。

过去十年,技术栈越来越厚,抽象层级越来越高,我们离底层越来越远。这本身不是坏事——生产力的提升靠的就是抽象。但问题是:当抽象出问题的时候,谁能解决它?

2024 年那次著名的 CrowdStrike 全球 Windows 蓝屏事件,影响 850 万台机器。根本原因是什么?是对 Windows 内核抽象层的理解不够深入,导致一个驱动更新直接干掉了整个系统。

这件事告诉我们:抽象可以让你跑得更快,但只有理解底层,才能让你在出问题时不至于束手无策。


给不同阶段的程序员的建议

如果你是大一大二学生:趁早开始 build-your-own-x 式学习。别急着学 Spring Cloud 微服务,先把 C 指针、操作系统、计算机网络学扎实。这些是你整个职业生涯的"地基"。

如果你是中高级工程师:每年选 1-2 个方向做深度实战。不需要把所有东西都造一遍,但要在你的核心领域(比如你是做存储的,就把数据库内核学透)做到「知其然,更知其所以然」。

如果你是技术 Leader:鼓励团队做「底层技术分享」。一个团队里如果没人能回答"这个问题底层是怎么回事",这个团队的技术判断力是堪忧的。


行动清单

  1. 今天:去 GitHub 搜 codecrafters-io/build-your-own-x,Star 它,通读一遍列表
  2. 本周:选一个方向(Redis/Docker/Git 三选一),找一篇高质量教程,开始跟着写
  3. 本月:完成一个「从零构建」项目,写一篇技术博客记录过程
  4. 今年:完成 3 个「从零构建」项目,你的技术深度会有质的飞跃

真正的技术深度,不是你知道多少框架的用法,而是当框架出问题的时候,你能不能把它修好。

build-your-own-x —— 49 万程序员用 Star 投票,投给了一份「真正掌握技术」的执念。希望你也能加入这份执念。


本文由程序员茄子撰写,欢迎转发分享。如有技术问题欢迎交流讨论。

复制全文 生成海报 Redis Docker Git 系统编程 底层原理

推荐文章

Vue3中如何处理组件间的动画?
2024-11-17 04:54:49 +0800 CST
linux设置开机自启动
2024-11-17 05:09:12 +0800 CST
api远程把word文件转换为pdf
2024-11-19 03:48:33 +0800 CST
php获取当前域名
2024-11-18 00:12:48 +0800 CST
Elasticsearch 聚合和分析
2024-11-19 06:44:08 +0800 CST
ElasticSearch 结构
2024-11-18 10:05:24 +0800 CST
程序员茄子在线接单