普通视图

发现新文章,点击刷新页面。
昨天以前首页

软件工程师面试: TCP/IP协议是什么?


最近,在面试第一轮抖音(字节跳动)的伦敦职位(Site Reliability Engineer),被问到了这个问题:TCP/IP协议是什么?这个是考基本功,是每个软件工程师都要会的。

TCP/IP(传输控制协议/互联网协议)是一组网络协议,管理数据如何通过互联网和其他网络传输。它是互联网的基本通信模型,由两个主要层组成:

互联网协议 (IP)

IP 负责将数据包从源地址路由到目标地址。它工作在 OSI 模型的网络层。

  • IP 地址:互联网中的每个设备都被分配了一个唯一的 IP 地址,用于标识数据包的发送者和接收者。
  • 数据包路由:IP 将数据分成多个包,并通过不同的网络将其路由到目标地址。
  • 版本:IP 主要有两个版本:IPv4(32位地址)和 IPv6(128位地址)。

传输控制协议 (TCP)

TCP 负责确保设备之间数据传输的可靠性。它工作在 OSI 模型的传输层。

  • 面向连接:TCP 在传输数据之前会在发送方和接收方之间建立连接。
  • 数据完整性:TCP 通过确认、序列号和错误检查等机制,确保数据包按顺序无误地到达。
  • 流量控制:TCP 通过滑动窗口管理数据流,防止接收方超载。

TCP/IP 协同工作原理

  • 应用数据:应用层将数据(例如网页、电子邮件)发送到传输层(TCP)。
  • TCP 层:TCP 将数据分段,添加序列号和错误检查信息,并将其发送到 IP 层。
  • IP 层:IP 层将 TCP 段封装成 IP 包,附上源和目标 IP 地址,并通过各种网络路由数据包。
  • 接收端:在目标设备上,IP 层将数据包交给 TCP,TCP 重新排列并验证数据的完整性,然后将其传递给应用层。

TCP/IP 套件中的其他协议

  • UDP(用户数据报协议):一种无连接、速度更快的 TCP 替代方案,常用于视频流、在线游戏等实时通信。
  • HTTP/HTTPS(超文本传输协议):用于网络通信的应用层协议。
  • DNS(域名系统):将域名解析为 IP 地址。

TCP/IP 确保数据在网络间高效传输,保持可靠性、地址分配和路由,同时遵循互联网的基本通信原则。

TCP/IP 通常被描述为一个四层模型,但有时它可以与 OSI 模型(七层)进行比较。

tcp-ip-and-osi-model 软件工程师面试: TCP/IP协议是什么? 学习笔记 程序员 计算机 计算机 软件工程 面试

TCP/IP 4层协议和OSI的7层协议的比较

TCP/IP 四层模型

TCP/IP 模型简化为四层,旨在反映协议在现实网络中的工作方式。

应用层

这一层对应于 OSI 模型的前三层(应用层、表示层和会话层)。它包括 HTTP、HTTPS、FTP、DNS 和 SMTP 等协议。

传输层

负责设备之间的可靠通信。运行于这一层的协议包括 TCP(传输控制协议)和 UDP(用户数据报协议)。

互联网层

处理跨网络的数据包路由,类似于 OSI 的网络层。该层包含 IP(互联网协议),用于地址分配和数据包路由。

网络接口层(或链路层)

这一层负责物理网络(如以太网、Wi-Fi)和互联网层之间的数据传输。它对应于 OSI 的数据链路层和物理层。

OSI 七层模型

OSI(开放系统互联)模型更加细致,将网络功能分为七个层次。

  • 物理层(如电缆、交换机)
  • 数据链路层(如 MAC 地址、以太网)
  • 网络层(如 IP 路由)
  • 传输层(如 TCP、UDP)
  • 会话层(如管理应用之间的会话)
  • 表示层(如加密、数据格式转换)
  • 应用层(如 HTTP、FTP)

主要区别:TCP/IP vs OSI

TCP/IP 将一些功能合并为更少的层次(四层),反映了它在互联网通信中的实际应用。

OSI 是一个更加详细的概念模型(七层),主要用于教学和理论理解。

总结来说,TCP/IP 通常被认为是四层模型,而 OSI 模型则是七层模型。

英文:What is TCP/IP (4 Layer vs OSI 7 Layer)?

面试经历

面试题

面试技巧

面试其它

本文一共 1009 个汉字, 你数一下对不对.
软件工程师面试: TCP/IP协议是什么?. (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c 软件工程师面试: TCP/IP协议是什么? 学习笔记 程序员 计算机 计算机 软件工程 面试
The post 软件工程师面试: TCP/IP协议是什么? first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  2. 孩子喜欢的 cozmo 机器人 小儿子今年刚过5岁生日, 问他要啥生日礼物, 他说想要 cozmo 机器人. 我们都不知道这是啥, AMAZON一搜还不便宜, 均价200多到300多英镑都有. 估计是孩子在学校的时候知道的这机器人. 在AMAZON下了单, 很快就到了. 图像识别算法很厉害. 这机器人很厉害, 不需要告诉它, 它可以自己玩,...
  3. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  4. 这些年在英国开过的车 这个车是真的汽车,从2010/2011年开始学驾照,到2012年考过驾照(两次才过),到现在也有十几年的驾龄了,真的算老司机了。 现在开的是两辆车(第四和第五),分别是奥迪Q5和保时捷卡宴。目前每周加保时捷的油费大概是50英镑。 第一辆 Seat Ibiza 第一辆:在英国的第一辆小黄车 Seat Ibiza (西亚特·伊比飒) 离合很重,男人开的车,当时用来练手,最后面到谢菲而得/Sheffield因为住在市中心不太需要车,就给卖了。 第二辆 奥迪AUDI A6 这辆开了有近十年,当时从谢菲搬家到剑桥Cambourne大剑宝就是开得这车。开了近10年的奥迪A6卖给了车厂(内含开车成本) 我当时买的时候是7000左右,然后买来修了修又多花了1000多英镑,...
  5. 同一台服务器上多个WORDPRESS站点的一些设置可以移出去 我自从把所有网站都挪到一处VPS服务器上 就发现很多事情省事很多 可以同时管理多个网站 包括 WORDPRESS博客. 比如我有四个WORDPRESS博客 然后我就把通用的一些资料给移出去 移到 HTTP或者HTTPS都不能直接访问的文件夹里这样就更安全许多. 文件 wp-conn.php 存储了 相同的数据库资料. 1 2...
  6. Win10右键添加管理员权限.reg 保存以下为 *.reg 文件 双击 Windows Registry Editor Version 5.00 @="获取管理员权限" "NoWorkingDirectory"="" @="cmd.exe /c takeown /f...
  7. 老婆的配偶签证被拒 郁闷死了, 601镑签证费打水漂,一去不回!费钱费力. 去年12月份我请了律师拿到了永居.老婆是T1G签证的陪工签 (DEPENDENT VISA) 2016年4月份到期. 然后我就想说得趁早把她的签证转成配偶签(SPOUSE)这样她就可以尽快走五年永居的路线. 今天收到拒签信,原因是我没有提供 有工资进帐的那份银行帐单,我提供了我和我老婆的联名帐户, 但是工资并不是直接打到这个帐单上的.所以就这一点被拒了.完全不给解释,不给补材料的机会.601镑就这样再见了. 英国的签证寄出之后是先由另一个部门先收费, 收完费才正式审理,而且不管结果如何是不退钱的.后悔没让律师弄,也不至于到现在浪费这么多时间和金钱,签证还没过.由于原签证还没到期,所以还不能上述.估计只能等搬完家后年底请律师搞定这事. 真是郁闷, 600镑, 我可以再买一个IPHONE6,或者给我的新买的车换四个轮胎....
  8. 公司给配了台高配DELL笔记本 早上例会结束的时候我顺便说了一句 我的笔记本有点慢, 当时我并不知道我的经理远程用电话也参加会议了(他全程在听), senior staff SE 对着电话说, “peter, you hear that? btw, my disks are...

C/C++ 中的内存管理器(堆与栈)


最近面试的时候遇到这个问题。这个问题考你计算机的基本功。

在 C/C++ 中,内存管理是控制程序如何分配和管理其资源的关键方面。C/C++ 程序中的内存通常分为不同的区域:堆栈和堆是最主要的动态和自动内存分配区域。

ACM题解系列之 – 最小堆栈 (Min Stack)

stack C/C++ 中的内存管理器(堆与栈) 学习笔记 技术 程序员 程序设计 编程 计算机 软件工程 面试

Stack 栈

堆栈内存

  • 定义:堆栈内存用于静态(自动)内存分配。它是存储函数参数、本地变量和返回地址的地方。当调用一个函数时,一个新的内存块(称为堆栈帧)会被添加到堆栈的顶部。当函数返回时,该内存会被自动释放。
  • 分配:内存由系统自动管理——在变量超出作用域时自动分配和释放。无需人工干预。
  • 生命周期:受限于函数或代码块的作用域。一旦函数退出,内存将被释放。
  • 大小限制:堆栈的大小通常较小并由系统预定义,意味着大的分配可能导致堆栈溢出。
  • 访问速度:由于其后进先出(LIFO)的结构,堆栈内存访问速度更快。由于内存是连续的且可预测的,它允许快速访问。
  • 使用场景:局部变量、函数调用信息和固定大小的对象(数组、结构体)。

堆内存

  • 定义:堆内存用于动态内存分配,程序员使用 C 中的 malloc()、calloc()、free() 和 C++ 中的 new、delete 手动分配和释放内存。
  • 分配:内存在运行时分配,并且分配的生命周期由程序员手动控制。它可以持续存在,直到显式释放。
  • 生命周期:堆分配的对象的生命周期不受作用域的限制。内存将一直被使用,直到被释放为止。
  • 大小限制:堆通常比堆栈大,但取决于系统资源。不当处理可能导致内存泄漏(忘记释放分配的内存)或碎片化(内存使用效率低)。
  • 访问速度:堆内存的访问速度比堆栈慢,因为分配是分散的,动态分配涉及更多的开销。
  • 使用场景:如链表、等大数据结构,或在运行时确定大小的对象。

堆与栈的主要区别

特征 堆栈
内存大小 通常较小,预定义 通常较大,受系统资源限制
分配 自动,由编译器管理 手动,由程序员管理(使用 new、malloc 等)
释放 自动(函数退出时) 手动(使用 delete、free 等)
生命周期 限于函数/代码块作用域 可以持续,直到显式释放
速度 较快(连续内存) 较慢(分散内存,开销更大)
风险 堆栈溢出(如果超出大小限制) 内存泄漏和碎片化

堆栈分配示例

void function() {
    int x = 10; // 分配在堆栈上
} // x 会自动释放

堆分配示例

void function() {
    int* p = new int; // 分配在堆上
    *p = 10;
    delete p; // 必须手动释放
}

正确管理堆内存在 C/C++ 中非常重要,因为它可能导致与内存相关的错误,如内存泄漏或重复释放。理解堆和堆栈内存之间的差异有助于优化程序的性能和可靠性。

英文:The Memory Manager in C/C++ (Heap vs Stack)

面试经历

面试题

面试技巧

面试其它

本文一共 874 个汉字, 你数一下对不对.
C/C++ 中的内存管理器(堆与栈). (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c C/C++ 中的内存管理器(堆与栈) 学习笔记 技术 程序员 程序设计 编程 计算机 软件工程 面试
The post C/C++ 中的内存管理器(堆与栈) first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. Javascript 中 sleep 函数实现 Javascript 中并没有 built-in 的 sleep 函数支持, 在 async/await/Promise 的支持之前, 我们可以用 busy-waiting 的方式来模拟: 1 2 3...
  2. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  3. 《Steem 指南》之 justyy 在线工具与 API 系列 – 同时给多个帐号发送SBD或者STEEM 同时给多个帐号发送SBD或者STEEM STEEMIT 和 BUSY 的前端都有一个内置的钱包工具, 您可以一次给一个帐号发送 SBD 或者 STEEM. 当我们要给很多很多人发送钱的时候, 就显得有些不方便了. 这时候可以用这个在线工具: https://steemyy.com/wallet-tool/ 填写表单 只需要填上你的ID,...
  4. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  5. 拔牙后的注意事项(图, 慎入) Care of Mouth after Extraction 昨天又拔了两颗牙, 初步定在5月4号装牙套. 这是牙医诊所给的术后注意事项: 拔完后需要等3-4小时麻醉失效后才能吃喝. 稍微流点血是很正常的. 但是请不要漱口吐出, 因为这会加速流血. 你只要轻轻的含着口水并咽下即可. 如果一直流血, 请拿着纱布(并不是纸巾)放在拔牙处20分钟. 24小时内请不要运动, 术后几小时内回家静静坐着. 12小时内不要吸烟, 喝酒或者喝热饮, 因为这会让伤口流血....
  6. 同一台服务器上多个WORDPRESS站点的一些设置可以移出去 我自从把所有网站都挪到一处VPS服务器上 就发现很多事情省事很多 可以同时管理多个网站 包括 WORDPRESS博客. 比如我有四个WORDPRESS博客 然后我就把通用的一些资料给移出去 移到 HTTP或者HTTPS都不能直接访问的文件夹里这样就更安全许多. 文件 wp-conn.php 存储了 相同的数据库资料. 1 2...
  7. 最简单有效的过滤WordPress垃圾评论的方法 当你的Wordpress博客流量大的时候, 不免会收到很多垃圾评论. 本文介绍一种特别简单而且免费的过滤Wordpress垃圾评论的方法. 这种方法不需要你安装任何插件, 也不需要拥有修改Wordpress主题模板函数的能力, 只需要1分钟就可以搞定. 把这个列表拷贝下来 打开 WordPress 的控制面版, 到设置-讨论 拷贝上面的列表到 “评论审核” 或者 “评论黑名单”...
  8. 更改全站的评论名称 坛子给我建议说: 我觉得很有道理,但是别人网站上的留言我改不了, 自己的还是可以先改改的. 于是,我登陆 phpmyadmin (一个网页式的php mysql 管理平台) 然后输入以下命令: update `wp_comments` set `wp_comment_author` = 'JustYY.com...

软件工程师经典面试题: 当你在浏览器的地址栏敲入google.com并按回车后发生了什么?


我认为这无疑是最受欢迎的软件工程师的(Software Engineer) 面试问题 之一。最近有人说这个问题曾出现在 抖音Tiktok 的面试中。

要回答面试中的“当你在浏览器中输入 https://www.google.com 时会发生什么?”这个问题,可以按步骤详细说明整个过程,涉及 DNS 查找、TCP/SSL 握手、请求处理和页面渲染。以下是全面的解释:

URL 解析

当你输入 URL https://www.google.com 并按下回车时:

  • 协议:浏览器识别出协议是 https,意味着它将使用 HTTP 加密传输(TLS)。
  • 主机:浏览器识别出 www.google.com 是域名。
  • 路径:默认路径是 /,因为没有提供具体路径,表示请求主页。

DNS 查找

浏览器需要将域名 www.google.com 转换为一个 IP 地址。这个过程分为几个步骤:

  • 浏览器缓存:浏览器 首先检查自己的缓存,看看是否已有 www.google.com 的 IP 地址。
  • 操作系统缓存:如果未找到,浏览器会向操作系统请求缓存。
  • 路由器缓存:如果操作系统没有该 IP,路由器会检查它的缓存。
  • ISP DNS 服务器:如果依然未找到,路由器会查询 ISP 的 DNS 服务器。
  • 递归 DNS 查找:如果 ISP 没有缓存 IP,DNS 服务器会递归查询 DNS 层次结构(根 DNS 服务器、顶级域名服务器、权威 DNS 服务器)。最终,www.google.com 的 IP 地址被解析出来(例如,142.250.72.196)。

建立 TCP 连接

知道 IP 地址后,浏览器需要与 Google 服务器建立连接,使用以下步骤:

TCP 三次握手:

  • SYN:客户端(浏览器)向服务器发送 SYN(同步)包,启动连接。
  • SYN-ACK:服务器响应 SYN-ACK(同步确认)包。
  • ACK:客户端发送 ACK 包,连接建立。

SSL/TLS 握手(针对 HTTPS)

由于使用的是 HTTPS,浏览器与服务器通过 SSL/TLS 建立加密连接:

  • 浏览器与服务器协商加密协议(TLS 版本)并交换加密密钥。
  • 服务器发送其 SSL 证书,浏览器验证该证书以确保服务器身份。
  • 生成会话密钥,用于加密接下来的通信。

HTTP 请求

建立安全连接后,浏览器向服务器发送 HTTP GET 请求:

  • 方法:GET
  • 请求头:包括浏览器类型、cookies 和缓存信息。
  • 主机:www.google.com
  • 路径:/

服务器处理

Google 的服务器位于 负载均衡器 后面,接收请求:

请求可能会通过多个反向代理和负载均衡器处理,通常分布在多个数据中心,以确保可用性和性能

Google 的 Web 服务器处理请求,检查所请求的资源(Google 的主页),并准备响应。

HTTP 响应

服务器返回一个 HTTP 200 OK 响应,并将必要的 HTML、CSS、JavaScript 和其他资源发送到浏览器。

响应包括响应头(如 Content-Type、Cache-Control)以及响应体(Google 主页的 HTML 内容)。

浏览器渲染

浏览器现在获取了 HTML 并开始渲染页面:

  • HTML 解析:浏览器解析 HTML 以构建 DOM(文档对象模型)。
  • CSS 解析:下载并应用任何链接或嵌入的 CSS 样式表以设置 DOM 元素的样式。
  • JavaScript 执行:下载并执行 JavaScript。JavaScript 可能进一步修改 DOM 或发送额外的网络请求(如 AJAX)以动态更新页面。
  • 渲染:浏览器的渲染引擎将解析和样式化的内容绘制到屏幕上,形成可见的网页。

附加资源请求

当浏览器解析 HTML 时,它会识别出额外的资源(图片、样式表、JavaScript 文件)需要加载:

这些资源通过额外的 HTTP/HTTPS 请求获取。这个过程会通过多个并行连接重复进行,以 快速下载和渲染资源。

浏览器缓存与优化

浏览器会根据缓存头(如 Cache-Control、ETag)缓存某些资源(图片、脚本、样式表)。

现代浏览器使用诸如 HTTP/2 多路复用等优化技术,通过单个 TCP 连接下载多个资源,从而减少延迟。

最终页面显示

一旦所有资源下载、解析和渲染完成,用户可以与完全加载的页面进行交互。进一步的用户操作(点击、输入等)可能会触发更多的网络请求(如提交表单、AJAX 更新)。

加分点

  • CDN(内容分发网络):Google 使用 CDN 从地理位置较近的服务器提供内容,减少延迟并提高加载速度。
  • 安全功能:HSTS(HTTP 严格传输安全)确保所有请求都通过 HTTPS 进行。Google 的证书绑定技术确保服务器的 SSL 证书未被篡改。
  • Service Workers:如果启用,Service Worker 可能会拦截请求,提供缓存响应或启用离线功能。

这份详细的说明涵盖了从用户输入 URL 到浏览器最终渲染页面的所有关键步骤,涉及 DNS、TCP/IP、TLS、HTTP 和浏览器渲染等内容,适合系统设计或软件工程面试。

英文:Software Engineering Interview Question: What Happens When You Type Google.com in the Browser Address Bar?

面试经历

面试题

面试技巧

面试其它

本文一共 1423 个汉字, 你数一下对不对.
软件工程师经典面试题: 当你在浏览器的地址栏敲入google.com并按回车后发生了什么?. (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c 软件工程师经典面试题: 当你在浏览器的地址栏敲入google.com并按回车后发生了什么? 学习笔记 程序员 计算机 资讯 软件工程 面试
The post 软件工程师经典面试题: 当你在浏览器的地址栏敲入google.com并按回车后发生了什么? first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 软件工程师面试: TCP/IP协议是什么? 最近,在面试第一轮抖音(字节跳动)的伦敦职位(Site Reliability Engineer),被问到了这个问题:TCP/IP协议是什么?这个是考基本功,是每个软件工程师都要会的。 TCP/IP(传输控制协议/互联网协议)是一组网络协议,管理数据如何通过互联网和其他网络传输。它是互联网的基本通信模型,由两个主要层组成: 互联网协议 (IP) IP 负责将数据包从源地址路由到目标地址。它工作在 OSI 模型的网络层。 IP 地址:互联网中的每个设备都被分配了一个唯一的 IP 地址,用于标识数据包的发送者和接收者。 数据包路由:IP...
  2. Meta的Enterprise Engineer企业工程师是什么? 和软件工程师的区别 我最近收到了一封来自 Meta 招聘人员的邀请邮件,关于 Meta 伦敦的员工企业工程师职位(Staff Enterprise Engineer): Meta 的企业工程师是什么? Meta 的企业工程师专注于设计、开发和维护内部工具和系统,以帮助公司员工提高生产力和效率。与传统的软件工程师角色相比,这一角色更偏向于内部,主要专注于为企业级需求构建基础设施、应用程序和自动化解决方案。以下是该职位的职责概述: 主要职责 内部工具和基础设施开发:企业工程师构建支持 Meta 内部业务运营的工具,例如...
  3. 测测你的幸运 – Linux Fortune-Teller LINUX 下有很好很好玩的命令,之前已经介绍过: figlet, rig, curl. 现在推荐另一个 命令 fortune 是用来随机显示一段(句)话的.fortune 在英文里就是幸运的意思. 这个命令可以不需要 参数 如果没有 可以通过 apt-get...
  4. 新的旅途 – 离别总是伤感的, 离开了一起创业的公司 2周前, 正式离开了一起创业的公司, 这公司是我博士毕业后的第一份正式工作, 待了8年多了, 离别总是伤感的. 我是9月初提的离职, 三个月 Notice Period, 最后的几周交接完工作确实没有什么压力了. 11月30号, 在公司最后一天, 公司有个习惯, 对于 Good...
  5. Minuet in C – 小步舞曲C Posted Youtube – 油管地址 孩子弹琴的时候最帅了. 我现在成了我儿子的粉丝了. Eric (Aged 6) is playing “Minuet in C” when...
  6. 上了年纪痛风脚崴了的惨痛经历(尿酸过高) 痛风是一种疼痛性关节炎, 当血液中的尿酸水平高, 导致晶体形成并积聚在关节内或关节周围, 就会发生痛风. 当人体分解一种叫做嘌呤的化学物质时, 就会产生尿酸. 嘌呤自然存在于您的身体中, 也存在于某些食物中. 尿酸通过尿液从体内排出. 上两周, 和媳妇吵架, 然后就自己一人睡, 有一天起床后脚踝就开始疼了, 然后明显比左脚肿了. 我刚开始就以为是睡觉的时候不小心姿势不对,...
  7. 优衣库 感觉像炒作 这几天 这个在北京三里屯 ‘优衣库’ 试衣间自拍的视频真的很火, 男女主角均被人肉. 不可否认 这个效果还真的不错 因为我之前根本不知道 “优衣库” 是干嘛的 很刺激 在试衣间XXOO是多么爽的事情 女主角 95后妹子 长相甜美....
  8. RMB人民币数字转大写汉字 – Javascript工具 Javascript工具RMB人民币数字转大写汉字 源码: https://justyy.com/js/atoc.js 最多只能计算15位, 小数点支持2位(毛和分). 最后一位分为0时, 需要加上’整’. 而且还需要在万亿,亿,万,元位等关键位0的位置写上’零’. 例如: 325.04 写成人民币 ‘叁佰贰拾伍元零肆分’ 人民币金额用到的中文大写汉字如下: 零~壹~贰~叁~肆~伍~陆~柒~捌~玖~拾~佰~仟~万~亿 壹佰贰拾壹万叁仟肆佰壹拾贰元整...

C++的 map 当键(Key)不存在的时候会发生什么?


面试流程(例如筛选)的早期阶段,一位 Google 招聘人员曾向我问过这个问题。

在C++中,当你使用std::map访问一个不存在的键时,行为取决于你是如何访问它的。

使用下标操作符 [] 访问时

如果键不存在,std::map 会默认插入一个该键的元素,并为其赋值为类型的默认值。比如,如果 map 的值类型是 int,那么它会插入该键并赋值为 0。

例子:

std::map<int, int> myMap;
int value = myMap[10]; // 如果键10不存在,会插入myMap[10] = 0

使用 at() 方法访问时

如果键不存在,at() 会抛出 std::out_of_range 异常。

例子:

std::map<int, int> myMap;
try {
    int value = myMap.at(10); // 如果键10不存在,会抛出异常
} catch (const std::out_of_range& e) {
    std::cout << "Key not found!" << std::endl;
}

使用 find() 方法

find() 方法不会修改 map,它返回一个迭代器。如果键不存在,它会返回 map.end()。

例子:

std::map<int, int> myMap;
auto it = myMap.find(10);
if (it == myMap.end()) {
    std::cout << "Key not found!" << std::endl;
} else {
    std::cout << "Value: " << it->second << std::endl;
}

C++ std::map 和 std::unordered_map的比较

std::unordered_map 处理不存在的键与 std::map 类似,但有一些差异,主要是因为它们内部的数据结构不同。

map 和 unordered_map 的区别:

  • 顺序:std::map 是有序的(内部实现为平衡树),所以元素会按键的顺序排列。而 std::unordered_map 是无序的,使用哈希表存储元素,因此没有特定的顺序。
  • 性能:std::unordered_map 通常有更快的平均访问时间(由于哈希结构,平均时间复杂度为 O(1)),而 std::map 的访问时间复杂度为 O(log n),因为其内部实现为树结构。然而,如果发生大量哈希冲突,unordered_map 在最坏情况下的时间复杂度可能是 O(n)。

总的来说,std::unordered_map 和 std::map 在处理不存在的键时,对于 []、at() 和 find() 的行为相似,但它们在顺序和性能方面存在差异。

总结

  • 使用 [] 访问时,如果键不存在,map 会插入一个新元素并赋予默认值。
  • 使用 at() 访问时,如果键不存在,会抛出异常。
  • 使用 find() 可以检查键是否存在,而不会修改 map。

英文:C++: Access a Non-existent Key in std::map or std::unordered_map

面试经历

面试题

面试技巧

面试其它

本文一共 473 个汉字, 你数一下对不对.
C++的 map 当键(Key)不存在的时候会发生什么?. (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c C++的 map 当键(Key)不存在的时候会发生什么? ACM题解 学习笔记 小技巧 技术 数据结构与算法 程序设计 编程 资讯 软件工程
The post C++的 map 当键(Key)不存在的时候会发生什么? first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 步步高学生电脑上 Basic 编程语言 peek 用法示例 步步高学生电脑 是8位FC机的经典之作.它上面的BASIC有三个版本 1.0, 2.0 和 2.1 2.1 版本有个在线帮助,实际上是 help.cmd 1.0 是用 Esc 键退回到 DOS 的,...
  2. 你给SteemIt中文微信群拖后腿了么? 这年头不缺算法, 就缺数据. 这两天花了很多时间在整API上, 整完之后自己用了一下还觉得真是挺方便的. 今天就突然想看一看自己是否给大家拖后腿了, 于是调用每日中文区微信群排行榜单的API, 刷刷拿着 NodeJs 练手: 1 2 3 4 5 6...
  3. Javascript 中 sleep 函数实现 Javascript 中并没有 built-in 的 sleep 函数支持, 在 async/await/Promise 的支持之前, 我们可以用 busy-waiting 的方式来模拟: 1 2 3...
  4. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  5. 《Steem 指南》之 justyy 在线工具与 API 系列 – 同时给多个帐号发送SBD或者STEEM 同时给多个帐号发送SBD或者STEEM STEEMIT 和 BUSY 的前端都有一个内置的钱包工具, 您可以一次给一个帐号发送 SBD 或者 STEEM. 当我们要给很多很多人发送钱的时候, 就显得有些不方便了. 这时候可以用这个在线工具: https://steemyy.com/wallet-tool/ 填写表单 只需要填上你的ID,...
  6. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  7. 试用 Linkedin (领英) 高级帐号 (Premium) Linkedin (领英) 算是比较靠谱的职业社交网站, 在上面有很多猎头, 很多知名公司的HR 无时无刻在招人. 特别领英在被微软收购之后, 名气就变得大了许多. 领英是免费使用的, 但也有付费用户, 有给猎头的, 也有给想找工作的. 价格并不便宜, 对于想找工作的 Job...
  8. 最简单有效的过滤WordPress垃圾评论的方法 当你的Wordpress博客流量大的时候, 不免会收到很多垃圾评论. 本文介绍一种特别简单而且免费的过滤Wordpress垃圾评论的方法. 这种方法不需要你安装任何插件, 也不需要拥有修改Wordpress主题模板函数的能力, 只需要1分钟就可以搞定. 把这个列表拷贝下来 打开 WordPress 的控制面版, 到设置-讨论 拷贝上面的列表到 “评论审核” 或者 “评论黑名单”...

银弹飞过先锋大厦

作者 hutusi
2024年5月28日 21:37
TL;DR 本文13200+字,全文阅读约需20分钟。其中2680字的示例部分为ChatGPT生成,因此可以说本文GPT含量20%,不过该部分详细内容与本文主旨关系不大,且由GPT-4生成,可略过不看。本文简要回顾了从软件及计算机诞生到当前大模型AI时代的软件工程发展历史,并试图从软件工程历史和ChatGPT实践案例中探讨在大模型时代的软件开发模式。

Code Review 的方法

作者 tison
2022年11月6日 08:00

开源社群可以通过开展广泛的开源协同来规模化开发软件的生产力。为了实现提升生产力的目标,一般而言需要解决两个阶段的问题:第一个阶段是如何找到适合的潜在开发者人群,吸引他们参与软件开发和社群建设;第二个阶段是社群的原始生产力上来以后,应对项目复杂度和沟通成本随人员规模平方级别增长的难题。

第一阶段的解决方案在《共同创造价值》当中有过相关讨论,第二阶段的问题可以通过工作的模块化和 Review 质量的提升来解决。本文即聚焦在开源协同当中的 Code Review 应该秉承什么原则,做到什么程度,如何避免常见问题来展开。

Code Review 的目的

《大教堂与集市》中有一个著名的 Linus 定律,“只要眼睛多,bug 容易捉”。这从某种方面体现了 Code Review 的价值,即发现代码补丁和主干代码当中可能的缺陷,在合并之前或合并之后予以修复,以保证软件的质量。

不过,Code Review 的价值远不止于发现代码缺陷,它至少还有两个重要的功能:减缓软件代码的熵增和传播项目社群的知识。前者从评估改动的必要性,改动方式是否与项目风格保持一致,以及保证代码的测试覆盖和文档覆盖等为切入点;后者主要是在 Patch Author 和 Reviewer 的交流过程中,传递社群成员共识的技术判断和做事方式。

Code Review 的内容

可以从开源社群常见的 Code Review 内容看出各个社群对 Code Review 的定位。

合理的动机

Code Review 的第一步并不是扎到代码里面去看写得对不对跟好不好,而是项目要不要做这个改动。

实际上,相当部分的参与者提交补丁之后迟迟未能得到 Reviewer 反馈的重要原因,就是决口不提为什么要做相关的改动,假设这一改动是社群共识。尤其是公司环境中被委派的工作,完成后想要将对应改动合并到上游,问及缘由只说是甲方或老板的需求,而不能从技术上或者广泛社群用户的角度上论证其价值,往往不会被上游社群所接受。

试想当你看到一个等待 review 的补丁,其中包含成百上千行改动,或者虽然只改了几行几十行但是意义不明,你还会花时间去思考其背后的深意吗?对于 Reviewer 来说,从整个项目的工程效率来看,遇到这种情况要么是直接不看了,要么是让补丁作者说明清楚动机和改动内容。只有判断这个补丁可能是有价值的,Reviewer 才可能分配时间仔细看具体的代码改动。

从另一个角度看,对于影响用户界面的改动,重大代码重构和功能提案,开源社群几乎总是要求提议者在提交代码补丁之前先撰写一个技术提案。Rust 是 RFC 机制,Apache Flink 是 FLIP 机制,Python 是 PEP 机制,诸如此类。

只有提案通过 Review 达成了一致,实现提案的代码补丁才有被 Review 的意义。提案本身不是代码,而是对要做的事情的动机的阐述和方案高度概括的设计。可以看到,Code Review 不是从代码本身开始做起的,而是先判断动机是否合理。

各个开源社群的实践中,PostgreSQL: Reviewing a Patch 文档就有一个 Do we want that? 的问题评估必要性,TiDB 开发者指南里也提到了 The pull request should resolve a real problem for TiDB users. 即改动必须有意义。

符合描述的实现

对代码补丁的动机和整体修改方案达成一致后,Code Review 的下一个阶段也是其核心阶段,就是检验代码实现是否与此前达成的共识一致。

Reviewer 进行 Code Review 的真相是这样的:首先,Reviewer 认同补丁的意图是合理的,随后,Reviewer 会在心里有一个预期的实现,对比这个预期的实现和代码补丁的实现,通常会快速略过和预期相同的代码,针对和预期不符的改动给出 Review 意见。

典型的情况是补丁的改动遗漏了某些特殊条件的判断,在配置的某种排列组合不能正确工作,或者引入了新的问题。这些情况下 Reviewer 会指出问题,负责的 Reviewer 还会尽量给出复现方式、技术原理和可能的改动方案。

为此,Reviewer 需要拉取补丁到本地,结合开发工具分析新版本代码,实际运行测试,以及按照自己的设想做出改动后再测试,最终给出需要改动的 Review 意见。

关于代码补丁的修改与描述一致,Flink 的 Review 指南有一点提到:

Does the Implementation Follow the Agreed Upon Overall Approach/Architecture? In this step, we check if a contribution folllows the agreed upon approach from the previous discussion in Jira or the mailing lists.

TiDB 开发者指南也提到:

The pull request should implement what the author intends to do and fit well in the existing code base.

夸奖优质的贡献

在上面提到的这个比对预期实现和补丁实现的过程里,除了发现补丁实现的问题,为补丁查漏补缺,还有一种情况,是补丁作者提出了意料之外的好方案。

老到的 Reviewer 不吝惜称赞亮眼的改动,虽然新人 Reviewer 可能会误以为 Code Review 的主题就是给补丁挑错,但是其实吸纳高水平的补丁,点出补丁作者做得好的地方,对社群发展有重要意义。这不仅是社群共识和软件质量螺旋上升的必经之路,对于补丁作者而言,得到正面反馈也是非常重要的一份激励。

曾经有位 Hadoop 的参与者在提交完第一个补丁之后,由于实现简洁明了而得到 Reviewer 的热情称赞,他受到强烈的正反馈持续参与,最终成为 Hadoop PMC 成员,并在不同场合分享这段经历,也传承了这个理念将更多新人带到开源参与的正向循环里。

关于正向激励的部分,TiDB 开发者指南有这样一段描述:

Offer sincere praise. Good reviewers focus not only on what is wrong with the code but also on good practices in the code. As a reviewer, you are recommended to offer your encouragement and appreciation to the authors for their good practices in the code. In terms of mentoring, telling the authors what they did is right is even more valuable than telling them what they did is wrong.

Python 开发者指南也有简短的一句:

Comment on what is “good” about the pull request, not just the “bad”. Doing so will make it easier for the PR author to find the good in your comments.

统一的代码风格

Code Review 的一大作用是减缓软件代码的熵增。

随着软件版本不断迭代,开发者们不断实现新的功能,引入新的抽象,就地修复代码缺陷。这些改动重重叠加,会把代码的复杂度也就是熵抬高到任何一个开发者都无法单独理解的程度。

控制代码复杂度的重要手段就是保持统一的代码风格。统一的代码风格能减少开发者入门的学习成本,跨越不同模块的理解成本,以及相互之间交流的沟通成本。

统一的代码风格,首要的一点就是功能实现的风格。

例如,几乎所有大型项目都会遇到不稳定测试的考验。这种不稳定性来到分布式系统领域,则主要是并发问题处理不当,尤其是没有统一的并发风格所导致的。

Apache Flink 的 Task Executor 进程在引入 Mailbox 设计重构之前,进程内的线程逻辑是犬牙交错的。开发者很难判断某个函数会在什么线程上下文当中被调用,也就不知道如何正确的在代码补丁中引用其他接口而不导致死锁。Apache Pulsar 的并发风格更是八仙过海各显神通,而开发者也最终要为这份代码熵付出巨大精力事后熵减的代价

反观开发超过 15 年的 Apache ZooKeeper 虽然并发工具的使用涵盖了直接使用 Thread 到 Executor 框架再到 CompletableFuture 组合子,但是核心的服务端处理逻辑和客户端通信逻辑的并发模型一直保持了连贯的风格,其他的并发工具使用范围都是局部的,这才造就了十余年来挑战者众多但是 ZooKeeper 一直保持分布式共识系统重要选型考虑的地位。另一个例子,Apache SkyWalking 的线程模型是核心模块统一设计、控制和调度的,其他模块通过接口提交任务,而核心模块调度保证这些任务不会出现竞争条件。这样,Code Review 的时候只要关注核心模块的线程变动,同时限制其他模块自建并发模型的范围,就可以很好地规避并发问题。

统一的代码风格,还包括语言范式和特性一致的的取舍。

典型的多范式编程语言,例如 C++ 和 Scala 都需要项目的核心开发者确定语言支持的各个范式和覆盖相同功能的不同特性之间的取舍。否则,一个 Scala 项目里部分代码对外暴露命令式调用方法,另一部分采用函数式编程提供一系列功能函数和组合子,在两个模块的交界处的代码就很容易产生不必要的胶水代码和引入难以分析的缺陷。同样,一个 C++ 项目既有 C 风格的函数尤其是对字符串和指针的用例,又有叠床架屋的模板代码设计出来的高度抽象的功能模块,这对跨越这两部分代码调试问题的开发者,就会引入极高的学习成本和理解成本。

哪怕是特性相对正交,几乎只有一条路实现特定功能的语言来说,这个问题也是存在的。例如,业内评价为暴力美学 Golang 即使代码写的差,也都是整整齐齐的“垃圾”。尽管如此,它还是面临错误处理风格没有语言级别约束的问题。前者虽然大部分 Golang 代码都是通过多返回值同时返回可空的错误和可空的结果来实现的,但是在 TiDB 代码里也有把 error 指针当成传出参数放到函数参数列表的 C 风格代码。类似问题放到 Java 代码里,虽然 Java 宣扬面向对象编程的范式,几乎凡事都可以通过抽象接口、不同实现,调用方只要调接口就行来建模,但是面向对象的几十个设计模式之间的取舍,也是风格不一致的诱发因素。

这是因为,无论语言是怎么设计的,不同的范式是客观存在的。对于熟悉 C 风格代码的开发者来说,他会在编写所有语言代码的时候都复用 C 风格代码的知识,在新的环境下找到熟悉的开发体验。这是难以避免的。对于 Code Review 来说,至少要保持模块内风格一致,模块间调用约定清晰,关系密切或说紧密耦合的模块采用相同的风格,而对于模块内部的实现,则不用锱铢必较。

统一的代码风格,最后是相同的代码格式和惯例。

例如,模块代入的是否有序、什么顺序,注释的风格,空格的风格,换行的风格。新生代的语言大多自带代码格式化工具,例如 Rust 的 rustfmt 和 Golang 的 gofmt 等,甚至 Golang 将某些传统代码格式的偏好固定成编译器检察项,例如 if 后左花括号换行就报错。对于早前的编程语言,包括 C 和 Java 等等,也都有业内共识度较高的工具,对应的是 clang-formatcheckstylespotless 等。

对于开源社群的协同开发而言,重要的不是选择什么风格,而是有一个确定的风格。当然,由于代码风格不适应严重的甚至会导致生理不适,一般而言所选的风格至少是核心开发者的共识。有了确定的风格,再将代码格式化作为提交前的必要检察项,这样就能减少由于不同人偏好的代码风格不同,导致提交上来的代码补丁总是包含形成干扰的格式变更带来的 Review 负担,以及格式化过程中难以控制的重构冲动导致引入新的缺陷。

最后这点,在 TiDB 开发者指南中强调为代码补丁需要专注于一件事:

Concentration. One pull request should only do one thing. No matter how small it is, the change does exactly one thing and gets it right. Don’t mix other changes into it.

显然,实现功能同时格式化代码,这就是两件事。

TiDB 开发者指南对代码风格还有另一段独立的说明:

Style. Code in the pull request should follow common programming style. For Go and Rust, there are built-in tools with the compiler toolchain. However, sometimes the existing code is inconsistent with the style guide, you should maintain consistency with the existing code or file a new issue to fix the existing code style first.

Flink 的代码风格指南则包括了上面提到的所有三个方面。

关键路径的性能因素

在知道算法的情况下,人是能像机器一样解决特定问题的,甚至依靠人的直觉和并行思考能力人的解决方案会更具现实意义。程序相较于人的突出优势,就在于处理重复的工作,尤其是不会出错的重复处理底层逻辑,并且这种处理速度远远超出人的极限。因此,程序的性能几乎是每个开发者心中的圣杯,每每会被提出来考量。

不过,Code Review 当中关注性能的部分,主要是关键路径上的性能因素。所谓关键路径,就是从输入到输出经过的延时最长的逻辑路径。

对于业务代码和实用工具来说,只要代码核心流程各个步骤不要出现时间复杂度的回退,尤其是不要出现与输入成指数级别时间复杂度的代码,基本上不会有太大的性能问题。

对于基础软件来说,处理的对象大多是更加底层的概念,例如单条数据记录或单个字节。在这些环节上就算出现常数级别的重复,在输入数据量的放大下,最终的时间和空间开销都有可能有显著的回退。例如许多 Java 网络系统都会做缓冲区零拷贝优化,就是避免语言运行时默认将网络传输过来的字节从网卡拷贝到用户空间再拷贝到 Java 堆上。从复杂度的大 O 记法来看,这只是常数级别的差异,但是在网络数据量级的放大下,这就是整个系统性能显著的回退了。

关于性能的 Code Review 工作,分析时间和空间占用的时候,不仅要看大 O 记法下的量级,还要考虑实际参数的取值范围和常数,两者综合的结果才是生产环境的实际性能表现。而在分析性能之前,要先判断相关代码改动是否是性能敏感的路径,或者补丁作者声称做出改动是为了改善性能。一个衡量性能优化的常用手段是提供基准测试(Benchmark)结果,这可以类比功能型修复对应的防止回退的测试。

各个开源社群的实践中,类似 TiDB 和 PostgreSQL 等以性能取胜的数据处理系统往往都会在 Code Review 指南中着重点出考虑性能方面的问题。而对于其他性能并非主要特性的项目,这类共识则一般隐含在 Reviewer 的共享知识当中,或者对于特定的几个关键路径会有注释或文档说明需要特别关注性能问题。

测试和文档

最后一个 Code Review 常见的内容是测试和文档。

虽然最后才提及测试和文档,但是它们在实现时却很可能在功能代码之前。上面提到的动机描述和设计文档,就可以算作是文档的一部分;而测试驱动开发的模式,测试是先于功能代码编写,随后实现功能或修复缺陷以通过测试。

测试和文档单独讨论都是一个内涵丰富的主题,在 Code Review 的语境下一并提出,是因为测试和文档的角度都是功能代码的用户。文档主要说明了代码实现了什么功能,调用约定和返回值内容都是什么,进一步的文档会提供代码使用的样例。测试则是功能代码的第一个消费者,Reviewer 和其他阅读源码的开发者都应该掌握阅读测试来理解一个功能模块或者整个系统的意图的能力。

TiDB 开发者指南提到 Code Review 中需要关注测试和文档的以下方面:

Tests. A pull request should be test covered, whether the tests are unit tests, integration tests, or end-to-end tests. Tests should be sufficient, correct and don’t slow down the CI pipeline largely.

Documentation. If a pull request changes how users build, test, interact with, or release code, you must check whether it also updates the related documentation such as READMEs and any generated reference docs. Similarly, if a pull request deletes or deprecates code, you must check whether or not the corresponding documentation should also be deleted.

值得一提的是,测试和文档并不是必要的,也不是越多越好。好的代码是明显没有错误的自解释的代码。只是把一眼看到函数名称,返回值和参数的类型和名称就能明白的内容写成文档其实是冗余的;测试显然正确的代码例如 Getter/Setter 也没有什么意义。

测试应该只检验模块的契约,也就是在不同类别的输入参数下,返回值和副作用是否符合预期。一个常见的测试误区是测试不属于当前模块的代码,尤其是测试外部依赖的逻辑。依赖模块的逻辑应该自己保证正确,下游只会在测试自身逻辑是发现上游不可靠,从而替换成新的实现或者向上游提交补丁,拉取新的版本。

Code Review 的暗礁

虽然大部分开源社群只有小部分成员才有向主干提交代码的权限,但是大部分开源社群都是鼓励所有社群成员参与 Code Review 的。从新人 Reviewer 成长为老到的 Reviewer 的过程中,有一些常见的 Review 技能以外的认识误区。本节从 Review 的几个常被忽略的真相出发,讨论如何规避 Code Review 的暗礁

Code Review 是一个交流的过程

Code Review 虽然有流程,但却不是无需人类活动参与的程序。软件工程没有银弹,同样也没有尽善尽美的代码补丁。程序设计几乎就是关于权衡(trade-off)的艺术,而 Code Review 就是 Patch Author 和 Reviewer 之间关于如何权衡的讨论。

不过,这种讨论又不是完全开放式的讨论。技术交流有一些行业或领域内的共识,正确性、性能报告和成规模的用户反馈实相对客观的。因此,Code Review 是一个技术事实和数据胜过主观感受和偏好的讨论过程。尽管 Reviewer 可能认为某个改动非常“脏”,但是在必要的性能权衡下,或者立即解决正确性问题的权衡下,没有更好的解法,也不应该出于个人主观判断否决提案。这是绝大多数社群都会要求的,给出 -1 的同时必须附带理由,否则 -1 无效。

此外,Code Review 的讨论是务实准确的。TiDB 开发者指南特别强调了这一点:

Asking questions instead of making statements. The wording of the review comments is very important. To provide review comments that are constructive rather than critical, you can try asking questions rather than making statements.

Provide additional details and context of your review process. Instead of simply “approving” the pull request. If your test the pull request, report the result and your test environment details. If you request changes, try to suggest how.

Python 开发者指南也提到,如果你在 Code Review 中检查了补丁确实具备什么功能,那么在 Approve 的时候也请带上相关信息,如果发现了问题,也尽量说明复现方式和环境。

其实这些原则贯穿开源社群的所有交流场景。把发现问题报告问题的原则放到已经合并的代码上,就成了 Issue Report 的原则;而把 Approve 的时候说明检查了什么内容,就变成了 Release Verification 的一个重要步骤。

最后,Code Review 通过交流传递知识。无论是补丁作者还是 Reviewer 提供了好的代码实例,还是交流过程中学习到了其他人分享的知识,都不要吝惜称赞。这种正反馈循环是开源协同长期运转的重要支柱。

开源社群的 Reviewer 都是志愿者

当然,Reviewer 有可能是因为受雇于某家公司才参与社群帮助 Review 的。但是这不妨碍从社群视角来看,开源社群的 Reviewer 都是志愿者。毕竟,如果你跟某个特定的 Reviewer 不在同一家公司,他对你而言是不是一个十足的志愿者呢?

Python 开发者指南中关于 Reviewing 的第一段话就是:

To begin with, please be patient! There are many more people submitting pull requests than there are people capable of reviewing your pull request. Getting your pull request reviewed requires a reviewer to have the spare time and motivation to look at your pull request (we cannot force anyone to review pull requests and no one is employed to look at pull requests).

所以,开源协同当中的 Code Review 以天或周为单位沟通合并是常有的事。为了提高自己在开源社群当中的效率,你不能死等在一个 Code Review 的反馈上,而应该尝试同时进行多个工作,哪一个给出反馈就调度上来再给一个回复。也就是说,开发者在开源协同当中像是一个并发的处理器。

从 Reviewer 的角度来看,你不是社群的雇员,更不是社群的奴隶。为了保持长久的参与热情和个人精神健康,认识到你是社群当中的一个志愿者至关重要:你不欠社群什么,社群也不欠你什么。当然,为了社群茁壮成长,掌握上面所有 Code Review 的方法,高效的完成 Code Review 也是社群生产力规模化的核心源动力。

Approve 意味着同意合并代码补丁

对于一个增长的社群来说,Code Review 是严格把关代码质量的一个重要环节。同时,通过 Code Review 向社群新成员传递的正确理念,将会极大提升他们后续参与的积极性和质量。

然而,如果 Code Review 标准太低,甚至出现为了某些 KPI 而选择先合并低质量代码再修复的策略,不仅损害了当下的代码质量,也会传递出一种类似破窗效应的信号,让其他社群成员误以为这个社群对待软件生产的标准就到这了。

Rust 标准库的开发者文档一开始就强调了 Approve 的意义和严肃性:

You are always welcome to review any PR, regardless of who it is assigned to. However, do not approve PRs unless:

  • You are confident that nobody else wants to review it first. If you think someone else on the team would be a better person to review it, feel free to reassign it to them.
  • You are confident in that part of the code.
  • You are confident it will not cause any breakage or regress performance.
  • It does not change the public API, including any stable promises we make in documentation, unless there’s a finished FCP for the change.

这其中,由于 Maintainer 有提交代码到主干的权限,他们的 Approve 会更加关键。实际上,这是一个选择 Maintainer 的标准。以 Apache 的权限模型为例:

For the committer bar, I always think of whether the candidate is easy to work with - make decisions with caution while bravely, knowing when to ask for help.

For too many new committers, it hurts when their contributions always need revision, especially trivial mistakes. If we elect a new committer while his/her contribution needs more attention to avoid merging wrongly quickly, we lose the reason to invite the very person.

Apache doesn’t set up fine-grained permissions so it’s extremely important not to approve something you’re unsure with.

当然,随着项目固有复杂性的增长,很可能任何单独一个 Reviewer 都不敢确定是否应该合并一个相对复杂的补丁。

面对这种情况,如果补丁是由多个连续的步骤,或者相互独立的几个部分组成的,可以让补丁作者拆分成多个提交。

如果确实不好拆分,则可以由多名 Reviewer 各自 Review 一部分,然后由经验丰富的一位整合 Review 意见。Flink 的 Review 指南的第三点提到:

Does the Contribution Need Attention from some Specific Committers and Is There Time Commitment from These Committers? Some changes require attention and approval from specific committers. For example, changes in parts that are either very performance sensitive, or have a critical impact on distributed coordination and fault tolerance need input by a committer that is deeply familiar with the component.

侧面来看,这也强调了 Approve 的时候带上自己检验过的内容,明确自己 Approve 的是哪一部分的意义。

❌
❌