前言

面的是 “基础架构-代码中台”,据说主要负责字节内部代码管理平台的实现。经过漫长的等待(7 天),终于等到了 offer,耶!

Eol 所说,等待时间长是因为我之前的面试表现并不好。如果我面试过程没有一丝问题的话,那应该立刻通知我入职才对。即使不是立即,也不应该拖了将近一周。在等待的这段时间里,公司那边也应该在做比较。特别是 7 月末、8 月初——字节提前批面试开始,HR 会把我和许许多多的提前批参试者比较。但幸运的是,我最后还是拿到了 offer

我是在 HR 面的时候才知道这个组是做这个的。由于这个岗位简直不能再契合我的想法,当时我听到后真的难掩心中的高兴。毕竟我想报复旦的软件实验室也是看上了其中软件组对基础软件的研究。给写软件的人写软件,简直不能更爽。

这次字节基础架构的面试是我第一次面大家口中的 “大厂”。当然了,也是我第一次参加互联网企业的面试,说不紧张、不激动肯定是假的。相较于实习面试的那些老手,那些 offer 收割机,我可谓是青涩了许多。

下面的 面试过程 部分都是面完后第一时间记录的,所以可能非常啰嗦,里面掺杂了很多个人情感。精简版的我会稍后放到 TJCS-Interview 里面。

个人情况

  • 简历上写的项目:
    • 一个前端搬砖项目,负责项目初期搭建和权限管理;
    • 一个数据库课设,从数据库设计到 UI 设计都亲历亲为的一个项目,前端套了个模板,后端差不多 90% 以上的代码纯手打,连 ORM 都没用;
    • 一堆平常写的小玩意儿,因为简历里面项目写了很大篇幅,所以问了我很多:
      • 一个难用的 RPC
      • 一个没有任何难度的数论计算器;
      • 等等……
  • 简历上写的竞赛:两个数学建模,后面证明了这个在技术面完全没用;
  • 简历上写得奖:一些普通奖学金,后面证明了这个完全没用(可能也就阿里会看吧,笑死);
  • 简历上写的经历:学校里面参加的一些组织,后面证明了这个在技术面完全没用。

面试过程

一面 65 分钟

一开始问了些项目上的问题。由于我把自己的数据库课设写上去了,面试官打算以我的课设开头。但那个项目是我一年前做的,现在忘得差不多了,于是立刻让面试官问我近期做的几个项目。

面试官对我自己写的一个 RPC 小框架有些兴趣,详细问了我写这个项目的动机(纯属个人兴趣+学习理论时的练手)、有没有其他人使用(没有),以及项目是为了解决怎样的现有问题(我就说是 protobuf 的序列化不是我想要的 + 自己想体验一下过程)

由于我在介绍自己写得这个小项目的时候提到了一致性哈希,所以面试官试探性问了我第一个技术性问题 “哈希结果在环上分配不均衡该怎么办”,我回答的是 “虚拟节点”。这也是我 60 分钟里面唯一不离谱的回答。

之后又聊了下自己做的其他两个小项目,引出了文件存储。面试官问我 “怎么存的文件”,我直接说就是文件存储。接着问了我 “如何处理具有相似前缀的文件的存储”,我觉得我不会,反手答了个 “我用 map 存的”,这答案把面试官整无语了。但我下来之后回顾这些问题,我想既然涉及了 “存储”,并且当时还是面试的场景下,要考虑的无非是 “查询的时间、空间”。对于这个相似前缀,我觉得最好的自然是建立适当的数据结构。存储不一定非得像 mysql 一样用 B+ 树,也可以用前缀树。我想这道题的答案就是前缀树,并且树节点后接链表(扩展文件系统的功能,使之处理可能存在的同名 & 不同作者文件,当然也可以只用前缀树,但按照用户划分不同区域)。只可惜当时我给出了一个非期望解法。

再接着开始聊我简历上的实习经历,但那个并不算严格意义上的 “实习经历”。面试官详细问了 “项目的应用场景” 和 “我如何与后端合作”。我吹我自己不止写前端了,有时候也给后端找 bug

接着还是从前端回到了我的数据库课设,因为我上面写了我是用的 mysql,就问我项目的数据库是不是 mysql cluster,我说是单节点的,写那个课设的时后还不懂集群。然后就问了 mysql 主从库之间如何做到同步,该用 mysql 的什么机制。这个问题拖了我很久,我一开始本来想说通过缓存刷新到另一个 mysql 里面,后面觉得不对,就说了触发器,再后面就开始发病了,连事务都拖出来讲了。但是面试官提醒我 “究竟是数据好,还是日志好”。那我肯定说日志好。接着就让我分析一下 “为什么日志好”。我想来分析得非常不好。

面试官开始问我语言问题,我简历上是按 c-cpp-python-go 来排序的,在我表明我不会 c-cpp 之后,就问我 “python 如何优雅地测试函数的运行时间”。我心里没底就开始乱答,什么 import time,unittest / pytest 就全说了。但面试官想问的其实是 “装饰器”。

再后面就是算法题,我拉了大胯,其实是一道很简单的题,但是没做出来。等面试官教完我算法,已经 60 分钟了。面试官表示很可惜。

最后是反问,我问为什么不深挖一个点,面试官表示 “那样你受不了”。我表示很感激。

一面面试官很和善,当面告诉我过了一面。我也发现他并没有特别刁难我,真的非常感谢。

总的来说有这些问题:

  • 项目:自己写的 RPC 框架中的协议应用了什么协议;
  • 数据结构:使用一致性哈希时,环上分配不均匀怎么办;
  • 常识:了解多少种哈希算法;
  • 项目:自己写的文件服务器如何存储文件;
  • 数据结构:如何处理大量具有相似前缀的文件存储;
  • 项目:项目的意义何在,具体应用场景是什么;
  • 常识:当前端的时候如何与后端 / 团队线上合作;
  • 数据库:熟悉哪种数据库,主从 MySQL 如何同步数据;
  • 语言:Python 如何优雅地测试函数的运行时间;
  • 算法:有序数组旋转后,求旋转后数组中的最小值;
  • 反问。

说句实话,我一面特地安排到了复旦夏令营开营的前一天。因为很久没有被面了,按着那种状态去参加复旦夏令营的面试,表现肯定比我最巅峰的那段时间差很多。所以字节的一面在当时的我看来,只是用来刷经验的……打一开始没期望自己过。当我和面试官纠结了 50 分钟还没写一行代码的时候,我猜测我一面必凉,但面试官觉得我回答还可以,是个好学生,所以就勉强放我过了。

二面 47 分钟

一开始也是让我自我介绍,我简单说了下我的项目经历,以及个人的学习情况。之前一面没有挖的太深,所以二面我做了一些准备。

首先是项目。

面试官相中了我所说的 golang 写的 “小 nginx”。并问我对 nginx 的组件有何了解。我只能说 “抱歉,抱歉,我是乱实现的,我不了解”。面试官接着问我 nginx.conf 中如何添加负载均衡节点,我也只能说 “抱歉抱歉”。可是最气的是,面试官问我 nginx 的状态码记得哪些,我也记不住,真的被自己气死了。没办法,的确太久没用过了,忘完了。只把面试官问的 “如何理解反向代理” 解释了一下,我觉得我对反向代理的概念还是理解得很清楚的。

面试官看到我的数据库课设自诩是 MVVM,问我对 MVVM 的理解。这个我还是有点理解,所以围绕 MVVM 的由来,以及和 MVC 之间的区别说了一些东西。我说前后端分离方便联调,之后谈起了我写项目的时候找 pythonbug。面试官问我 python 中的 is== 有什么区别,我当时表示不会,但是我根据平常的使用经历猜了下它们分别用于比较地址和值,面试官直接问我是不是真的不知道哈哈哈哈。

接下来,面试官开始说我的前端项目,我在前端项目的介绍中说自己是做 “权限管理” 的。我跟面试官解释了一下 “我做的是前端权限管理,不了解后端的权限是怎么写的”,接下来说自己使用的 RBAC。面试官问我 RBAC 具体是什么,以及如何考虑 RBAC 在项目中的应用,RBAC 的优势是什么。我就解释了下 RBAC 的字面义,以及我前端项目中的 5 个角色。RBAC 的优势我是用 ABAC 做了个对比,说了下 RBAC 在编程设计、数据库设计上的简易性,以及在项目传承上的可理解性。

面试官听我说 “RBACAttribute 抽象成角色” 就问我在编程中最好使用哪种设计模式来做 RBAC。我秒答工厂模式。面试官应该对我这个答案很满意,然后让我写一个简单的工厂模式。可惜我没写出来。(但事实证明我马上就要写出来了,只是我觉得我没写出来)

面试官自然很失望,又看到我写的 “小 RPC”,就问我 HTTPRPC 之间的差别是什么。这个问题我也答得不好,只说了 Protobuf 生成的协议比 HTTP 协议短,还说 RPC 可以自定义,所以程序员想咋写就咋写,但是 HTTP 总有那么一套协议规则,全球人都得遵守,我属实开始乱答了。面试官问如果有一个系统,又用 HTTP 又用 RPC,那么什么情况下用 HTTP,什么情况下用 RPC。我也没开发过大型系统,所以根据自己对它们概念的理解,回答内部用 RPC,对外借口用 HTTP,但是下来之后搜了下,发现自己又猜对了。我在回答 HTTP 的时候说了句 “统一的标准”,面试官问了我该如何设计基于 HTTPAPI,我回答 REST-ful,并且按照要求解释了一下 REST-ful 以资源为主体的核心思想。

面试官接着问我 HTTPSHTTP 的区别。我说是有 SSL/TLS 的加密,但我忘了这个玩意儿具体怎么加密了,所以只能发挥我在 “信息安全原理” 里面学到的 SSLv3 算法协商,把什么 HMAC, RSA, AES256 什么的一口气砸了过去。毕竟我看过一点 OpenSSL 的代码,所以面试官估计是被我套晕了,所以立刻换了个话题。

接下来就是算法题。给一个二维数组,然后统计区块,或者可以叫做 “岛屿计数” 问题。我 bfs 一气呵成,直接通过,都没 debug。估计也是这个题一口气写出来才让我低空飘过二面。

再之后就是问我暑假的安排和下一学期可能的学习情况,暗示我实习时间不要太短。我自然求生欲拉满。

最后是反问,我整了个活儿,直接问了面试官一个我一面的问题哈哈哈哈。

二面面试官人很好,听到我这也不会那也不会,比我还先一步戴上痛苦面具。后面一直通过我的项目给我找话题,问的也都很简单,我真的非常感激。

总的来说有这些问题:

  • 常识:对 nginx 的组件有何了解;
  • 常识:如何理解 nginx 的反向代理;nginx 中添加负载均衡节点的语法是什么;
  • 常识:nginx 都有哪些状态码,5 开头的表示什么,500 / 502 / 503 是什么意思;
  • 常识:如何理解 MVVMMVVM 是做什么的,MVVM 对于团队开发有什么优势和劣势;
  • 语言:Python 中的 is== 有什么区别;
  • 项目:项目中如何实现权限管理,项目中的 RBAC 是如何做到的,优势是什么;
  • 设计模式:手写工厂模式,工厂模式的特点是什么;
  • 常识:HTTPRPC 的差别是什么,如果一个系统同时使用 HTTPRPC,二者会被分别应用于系统哪几类接口上;
  • 常识:介绍 REST-ful 的思想以及 REST-ful 风格 API 设计方式;
  • 常识:HTTPSHTTP 的区别,SSL/TLS 是如何加密的,简述 HTTPS 握手,恶意第三方如何破解 HTTPS
  • 算法:岛屿计数问题;
  • 未来一段时间的安排情况和实习可能时间;
  • 反问。

二面是 7 月 9 号,在复旦夏令营结束之后。由于那天约了同学晚上吃饭,所以心思都到吃饭上去了,根本没在这个面试上。所以也没怎么准备,加起来也就准备了 2 个小时吧。其实问的都是很简单的问题,但是我答得并不好,吃饭的时候自然是觉得自己凉凉了。

和同学聊天的时候,就在懊悔——既然自己复旦没过,就应该把后面的时间全放在这个面试上才对,不要因一时的失败而影响后面的生活啊。我当时在想,复旦优营没拿到已成事实,可如果我二面过了,三面也侥幸通过,那我岂不是有机会真正见识互联网企业了吗?无论我读研还是不读研,最后的去向应该都是互联网企业,这样一个机会肯定要抓住才是呀。

虽然和同学约饭非常令人愉悦,但刚刚经历的面试的确无法让我毫无顾虑地笑。但没想到饭都还没吃完,就被告知自己通过了,属实是意外之喜。

放下电话,我又惆怅了。准备了很久的复旦夏令营却草草收场,没怎么准备的实习面试却过了二面。我只能感慨。

而我也是从那个时候,开始非常重视这次机会,老老实实准备三面……

三面 50 分钟

我的三面很奇怪,并没有问任何基础知识和项目细节,与其说是面试,不如说是唠家常。看之前学长学姐以及同届同学的面经,三面都会问一些比较困难,但与实际应用场景有关的问题。我大意了,没有闪。

开场自我介绍,接着就开始聊天,比如问了下我为什么下学期时间那么多,问我平常专业课都学了些什么。

这位面试官根本不问我细节的问题,第一个正经的问题是问我平常什么 app 用得最多。我当时听到这个问题真的心脏骤停了。很难想象,我的手机里一个字节的 app 都没有……没错,无论是我面的头条,还是抖音、西瓜、火山、飞书、皮皮虾、轻颜等等,别说用了,我一个都没下载过。于是我哆哆嗦嗦地问了句 “电脑上的应用软件,或者说网页应用算吗”。面试官说都算在一起。我如释重负,开始和面试官谈 GitHub 在团队管理功能上的缺点。

在我介绍了我写的几个项目之后,面试官问我为什么会写那些项目。由于这个问题已经被问过很多次了,所以我也答得比较顺利——“我平常会观察其他学校甚至说其他国家的同专业学生,看他们在学习我所学习的课程时完成的作业和项目。我通过这个方法,了解到了许多我们学校由于课时、资源等原因未能开展的课题。所以我在课余时间会学习一些本校课本之外但我觉得却十分必要的知识。学习理论是极其重要的,理论在计算机领域内能起到非常重要的作用,但是实践也极有价值,必要的实践可以巩固理论基础。我的每个小项目都有不同的地方,我在设计实现每个项目的时候,都希望自己可以学到一点新的东西。我是真的热爱,并在不停地探索,这应该是我比较不同的地方。”

面试官之后试探性问了下我写的项目有没有人用,我坦白说大多没什么人用。面试官接着问我为什么能够坚持写没有人用的项目。我觉得这个问题我答得特别好,我说:“我打心底愿意为其他人写代码,愿意用我的代码帮助其他人。其实我的项目不能说 ‘完全没有人用’。无论是我暑假抽空写的数论 / 密码学计算器,还是反汇编脚本,都还算有一些用户,并且我也知道,我的项目在某种程度上帮助到了他们。哪怕我写了 1000 行代码,只要有一个用户觉得好用,或者说他觉得不好用,但我基于他的建议改进后让他满意,那我也觉得一切都值得。”

随后面试官问了几个通用技能问题。比较有意思的是问我 “一篇好的文档应该由几部分组成”。我先叙述了我认为的广义上的文档是什么,然后用当时能想到的所有分类方法叙述了一遍,比如对内文档和对外文档;代码注释和代码外文档;文字、图片和视频等等。拿文字文档举例,当时我说的就是 Intro / Feature / Usage / Build / Example / Version / ChangeLog / Contributor 这些组成部分。最后问我 “有没有利用注解自动生成过文档”,我觉得这问的太明显了,随便说了个 Swagger UI,最后我表达了下自己对这类文档生成工具的鄙视。

总共前面问了我 28 分钟,然后给我出了一道经典的 leetcode hard。很可惜我没做出来。

总的来说有这些问题:

  • 家常:平常用得最多的应用是什么?用的时候觉得它最大的缺陷是什么?你作为使用者,是如何规避这个缺陷对你带来的影响的?
  • 项目:为什么会写这些项目?项目使用人数多吗?如果没有人用,你为什么要坚持写这些小项目?你一直以来的动力是什么?
  • 项目:做项目的时候写文档吗?觉得文档最重要的是什么?文档应该由几个部分组成?读过哪些优秀的文档?它们的特点是什么?有没有用 Java 注解生成过 api 文档呢?
  • 项目:做项目的时候写测试吗?测试都有哪些方法?
  • 算法:二叉树的最大路径和。

当时觉得自己凉了。觉得面试官就是想 “劝退” 我——先问一些没有区分度、写过项目就能回答上来的问题,然后再来一道我做不出来(其实是我太菜了)的算法题。

一面是面试官当面告诉我结果,二面是过了不到一个小时就有回复。而三面结束后,我隔了两个小时给 HR 打了个电话,却被告知 “大概 3 到 5 个工作日” 会出结果。很明显我凉了。

当天晚上,我躺在床上,心里没想什么人生,没想什么未来,只是想着 “终于可以回家了”。夏令营结束,实习面试也结束,整个 7 月着实让我 exhausted,哪怕回家待不了几天,那也回去待一会儿吧。等到拒信明确,我再找我爸商量机票的事儿。

但谁成想一觉醒来,洗了个澡,我三面就过了。

HR 面 65 min

我非常诚实,这 65 分钟没说半句假话。

我自认为把一些正常来说难以想象、难以实践的事情,在这几轮面试里做了个遍:

  • 调一面时间,我直接问 HR,之前没约一面时间,所以面试时间可不可以调到 14 分钟之后;
  • 调二面时间,网站上给出的时间段我全在夏令营,干脆打电话给 HR 调到周五下午 5 点这种不可能有人面试的时间;
  • 鸽三面,我把时间记错了;
  • 鸽了之后调三面时间,跟 HR 商量能不能再给我次机会;
  • 调好之后发现时间有冲突,没过几分钟又给 HR 打电话调时间;
  • 三面结束后迫不及待给 HR 打电话问自己过没过。

但这些再怎么离谱,我觉得都不如 65 分钟的 HR 面没说半句假话

面试时,我的求生欲是源自内心的、对车补餐补的疑问是源自内心的、对请假流程的试探是源自内心的、对自己性格 / 经历 / 未来发展方向的回答也是源自内心的。我是个怎样的人,我展现出来的就是一个怎样的人。

当时自然觉得自己很帅。但正因为我说的都是一点都没有包装的大实话,所以现在反而担心 HR 面把我挂了。(啊这

需要注意的是,嘉定校区离字节太远了,通勤时间非常长,据 HR 所说来回就是 3 个小时。所以 HR 很有可能会问 “是否已经确定好住处”,好像听起来 “是否确定住处” 和 “发 offer” 还是有一定关系的,这有些让我感到迷惑。