我这一生都对技术充满了热情。 我小时候从 Turbo Pascal 起步,此后还做过嵌入式系统、前端/后端开发以及机器学习项目。
在大学里一门短期课程中,我开始接触 iOS/macOS。 不到一个月,我就学会了一门新语言,并在 Readdle 获得了一份实习机会。 五年后,我成为了一名首席工程师。 即便如此,我仍在指导新同事、参与面试,并培养领导力,以持续成长。
从对 Apple 平台毫无经验,到走到今天这个岗位,这段经历促使我回顾自己的学习方法,并梳理出那些帮助我成长的关键方向。 我相信,我的经验对那些感到停滞不前,或不确定下一步该怎么走的工程师来说会有价值。
下面是帮助过我的一些关键方法和资源,也许同样能帮助到你。
业余项目如何加速了我的成长
达到中级水平后,我遇到了职业上的平台期。 我感觉自己能又快又高效地完成任务,修复 bug 也毫无问题,对如何为新功能设计架构也有了扎实的理解。 但我始终没太弄明白,究竟是什么让我还算不上真正的高级工程师。
这种转变是逐渐发生的。 回过头看,我意识到差别并不在于工作中的某个大项目,也不在于一次晋升。 而是在工作之外,我投入的大量编码时间。
在晚间和周末,我会做一些小项目,有时只是为了好玩,有时是为了验证一个想法。 这些业余项目让我接触到了超出我日常范围之外的真实问题。 这种亲自动手获得的经验直接转化到了我的工作中,也让我在处理陌生问题时更有优势。
我学到的最大一课是,软件开发中的大多数技能并不局限于某一种语言或平台。 举几个例子:
- 即使你读的是 iOS/macOS 应用架构相关的内容,你也很可能会在 Web 开发或嵌入式系统中发现类似的模式。
- 在机器学习场景下掌握的 Python 知识,可以帮助你在一个完全不同的领域里,快速写出自动化 CI 流程所需的脚本。
- 操作系统基础能帮助你理解跨越任何编程语言的一整类问题。 毕竟,无论使用什么语言,任何程序归根结底都会以相似的方式与操作系统交互。
- 我最早读过的书之一是《Effective Java》——一门我已经很多年没有使用过的语言。 但那本书教会了我适用于各个领域的面向对象原则。 这才是那种真正能沉淀下来的知识。
- 如果你喜欢编程,这些业余项目就不会让你觉得像是在工作。 以我的经验来看,最优秀的工程师,都是因为热爱构建和学习而写代码的人。
学习任何事的方法
有些人似乎学得比别人更快。 这可以是一门新的编程语言、一个模式、一种架构,甚至是与开发完全无关的东西,比如学下棋。 我相信这是因为,学习本身就是一种技能——一种你可以刻意训练的元技能。
回顾我自己的学习历程时,我发现自己在接触新主题时,总会反复出现一些模式。 我觉得这源于我在中学和大学时期的经历:同时处理多门学科,迫使我的大脑去优化类似的模式。 这让我学得更快。
现在,我已经形成了一套学习新事物的具体流程。 我不认为这个过程有多么突破性,但它确实让我在学习和自我成长中始终保持专注与明确的方向。 在这一部分中,我会带你了解我的学习方法的三个阶段:筛选、结构化和实践。
筛选
在学习一项新技能时,找到优质的信息来源是至关重要的第一步。 以下是一些基于我经验的建议:
- 几乎任何主题的信息,大多都能免费获得。 除非你非常确定内容具有独特性,否则通常没有必要为课程或讲座付费。 但如果你才刚起步,你还不具备足够的判断力去分辨真正有价值的洞见和低质量材料。
- 与视频相比,文本类资源通常更精炼,也更容易消化,尤其是当你阅读速度很快时。 随着时间推移,我学会了快速略读并筛掉无关信息,而这在视频内容中要困难得多。
- 随着时间推移,我培养出了略读并筛除无关信息的能力,而这在视频材料中几乎做不到,因为视频是线性的,而且很耗时。 而且,在文本中给某个片段加书签或直接复制,也比在视频里寻找对应时刻容易得多。
- 英文资源比其他语言的资源更容易找到,尤其是在编程和技术领域。 因此,能够快速阅读并理解英文是必备技能!
- 不要跳过经典之作。 那些被反复推荐的书(如《Clean Code》或《Design Patterns》)往往依然是最好的资料。
- 我也建议你建立一个涵盖不同主题的个人藏书库。 这样一来,你手边就总会有可靠的资料可供参考。 就我个人而言,我会在电脑上保留一个简单的文件夹。
结构化
当我收集到几份扎实的资料后,我就会开始整理这些信息。 这个过程包括:
- 处理资料。 在阅读书籍或文章时,我会用书签标出关键点,或把它们复制到笔记里,方便将来查阅。
- 交叉验证信息。 尤其当主题具有主观性时,对比多个解决方案和不同视角非常重要。 如果一条信息在多个不同来源中都一致出现,那它很可能是真的。 当不同来源彼此冲突时,我通常会暂缓下判断,直到自己对这个主题积累了更多亲身经验。 之后,随着理解加深,我就能选择出最佳方法。
- 集中管理知识。 为了分析所有接收到和找到的信息,需要把它们存放在同一个地方。 我使用 Obsidian 来集中管理我的知识。 它是免费的,而且我觉得 markdown 格式非常方便。
- 存储。 我通常会用简单的 markdown 列表保存文章和有用的链接。 这能帮我快速找到某个特定资源,而不用去翻浏览器历史记录。 此外,如果以后我再次回到这个主题,我也已经保存下了一个不错的基础。
实践
光靠阅读是不够的。 只有当你真正去做出东西时,你才算真的学会了。 我会尝试通过创建小项目来巩固我学习的每一个主题。 说到编程,这通常意味着做一个简单的应用,来展示我是否能够运用所需的技能。 快速实践也会立刻暴露出理解上的缺口。 我常常以为自己已经理解了所需的一切,却在实现开始仅仅 10 分钟后,就遇到了意想不到的问题。
其中一个让我印象深刻的例子,是学习 UI 架构。 当我第一次在 iOS 开发语境下学习 MVVM 时,我觉得这种架构简单直接。 但当我尝试做一个基础的待办清单应用时,我立刻就遇到了知识盲区。 资料解释了 View 如何与 ViewModel 通信,以及 ViewModel 如何与 Model 交互,但没有任何资料说明究竟该由谁创建什么、又该如何创建!
我的应用里有一个表格,用来显示当前的待办任务。 我应该为每个任务分别创建一个 ViewModel 吗? 还是为整个表格只创建一个 ViewModel? 如果我两种 ViewModel 都需要呢? 那该由谁来创建哪一个? 这些问题为我打开了大门,让我开始理解在类似架构中该如何协调各个组件。
后来,在参与 PDF Expert 的开发时,我们团队决定尝试实现 MVVM。 我得以搭建这套架构,并帮助带领团队完成该功能的开发。 如果不是我当初先自己做了那个虽小却有些混乱的待办应用,我就不可能获得这样的经验。
我对业余项目的建议
- 寻找灵感和点子。 通常是先有想法,然后我再把它和一个具体的学习目标结合起来。 这个点子不必独特,甚至不一定特别有用,因为最主要的目的还是获得经验。 例如,我可能会决定做一个习惯追踪应用。 接着,我可以选择专注于架构,或者借这个点子设计一个带动画效果、令人印象深刻的 UI。 又或者,也许我需要为这个应用搭一个服务器? 那会是尝试新后端技术的绝佳机会。
- 定义“完成”。如果你试图把每个项目都做到可投入生产环境的程度,那你必须为每个项目投入大量时间。 因此,我认为你需要为每个项目定义你想达成的里程碑。
- 完美是优秀的大敌。 如果你过于执着于寻找“完美”的解决方案,你可能会彻底失去动力。
- 尽量减少不必要的任务。 如果一个应用的目的是让你学习架构,那精确的配色方案真的重要吗? 或者日志系统呢? 最有价值的项目,往往是那些只专注于一个学习目标的项目。 我花在低优先级细节上的时间越少,就越能把精力投入到真正重要的事情上。
10 个改变我构建软件方式的主题
以下是我自行探索过、并对我的职业发展产生影响的 10 个领域。

UI 应用架构
任何做过 UI 类项目的工程师,都熟悉那些经典的代码组织模式。 谁没听说过 MVC、MVP、MVVM 呢? 以我的经验来看,简单的理论知识可以给你开启新项目的信心,但如果没有实践经验,它往往会在复杂性刚一出现时就崩塌。
我曾经以为自己已经完全理解了 MVVM 的工作方式,但我并没有真正实践它的经验。 当我尝试开发一个小型 MVVM 应用时,我意识到了这一点。 我很快发现,我需要 coordinators 和 routers,才能在导航和组装层面,把那堆杂乱的类整合成一个连贯的结构。 那次经历让我学到一个关键教训:根本不存在一种放之四海而皆准的架构。 一种在某个项目中(甚至某个页面中)运行良好的架构,放到别处可能就很不合适。
设计模式
任何经历过开发者面试的人,很可能都遇到过有关设计模式的问题。 许多工程师似乎在理论上认识某些模式,但在实践中却很难真正运用它们。 T弥合这一差距最简单的方法,就是做一个专注于实现某种特定模式的小项目。
这本经典原著经常以文本编辑器作为示例。 以我的经验,不同类型文档的编辑器会需要用到很多设计模式。 它可以是文本编辑器、图像编辑器,甚至是绘图程序。
测试
代码测试是稳定代码库的基石。 虽然这个想法看起来很简单——“只要写测试!”——但写出真正有用的测试往往远非易事。
最大的挑战在于,维护测试会消耗团队资源。 如果每次代码重构后你都不得不重写测试,那么这些测试的整体价值就值得怀疑了。 这个概念被称为 Test Fragility,而根据我的经验,它并没有得到应有的重视。 例如,你可以通过这本书来学习测试理论。
对测试的高度重视也会影响代码质量。 总体来说,以可测试性为前提编写的代码会更加模块化,并暴露出更干净、更简单的 API。 在实践中尝试测试其实很容易——只要把测试集成到现有项目中,或者新开一个以编写测试为重点的项目即可。
算法与数据结构
在现代软件开发中,懂算法并不是硬性要求。 许多公司仍然会在面试中加入算法题,尽管这些技能在日常工作中未必会用到。 即便如此,我依然认为值得投入时间学习算法,因为这既有趣又好玩。 此外,如今你还可以通过像 LeetCode 这样的平台,以交互方式探索这些主题。
如果你有机会参加 ACM ICPC 这类算法编程竞赛,我强烈建议你试一试。 即使这些知识起初看起来并不直接有用,代码优化技巧也可以应用到任何项目中,而参加团队竞赛的经历也很可能会成为你日后珍视的回忆。
多线程
多线程是一个非常迷人的主题。 你可以写很多年代码,却仍然不理解其中哪怕最基础的概念。 每个开发者都知道,阻塞主线程会让应用卡住。 但一旦出了问题,只有那些真正理解多线程代码、并能在脑中还原完整事件时间线的开发者,才能诊断出那些难以捉摸、如幽灵般的 bug。
在我看来,理解同步原语、线程,以及忽视这些概念会带来的后果,是区分中级开发者和高级开发者的关键因素之一。 要理解这些基础概念,我推荐免费书籍 The Little Book of Semaphores。
想获得实战经验,你可以尝试编写一个执行大量后台处理、并将结果与 UI 同步的程序,尤其是当后台工作本身还涉及多个线程时。 模拟物理过程会是这类项目一个很好的起点。
图形编程
图形处理器是计算机中少数几个大多数开发者很少需要直接打交道的部分之一。 对于大多数应用来说,UI 渲染要么发生在 CPU 上,要么被抽象到无需直接面对实际 GPU 设备的层面,因此你甚至不需要去考虑它。 但一旦屏幕上的元素数量增长到足够多,而高层抽象又无法跟上所需的帧率时,你就需要更深入地理解它。 理解 GPU 被设计用来解决哪些问题,以及如何向它们“说明”需要渲染什么内容,能帮助你在类似场景下快速找到解决方案。
学习图形编程的经典项目之一,就是构建一个游戏引擎。 如果你像我一样热爱电子游戏,你大概也曾好奇过它们是如何被编程实现的。 网上有大量资源可供学习 Metal、DirectX、OpenGL 或 Vulkan。 只是别指望你的项目会成为下一个 Unreal Engine。 但如果你能做到让一个小游戏运行在自己编写的引擎上,我敢保证,你一定会遇到很多令人着迷的挑战,并获得宝贵的知识。
嵌入式编程
当我第一次了解到,我们身边哪怕最小的电子设备也运行着自己的程序和处理器时,我立刻就被深深吸引,想亲手做一个类似的东西。 不过,面向微处理器开发会带来许多限制。 这类系统通常只有不到 128 KB 的内存,而且动态内存分配通常也不可行。
编写和调试嵌入式代码会面临很多挑战。 但作为回报,你可以看到自己的代码在现实世界中“活”起来。 哪怕只是让一个简单的 LED 闪烁起来,对我来说都像是一次突破。 理解微控制器的工作原理,是理解现代 CPU 和操作系统如何运作的重要第一步。 在处理嵌入式设备时,你甚至可能需要接触平台特定的汇编语言(通常是 ARM)。
如今,入门嵌入式开发比以往任何时候都更容易。 像 STM32 系列这样价格亲民的微控制器,就是很好的起点,而且许多现成开发板都自带可通过 USB 连接的编程器。 从动态房间照明到搭建你自己的智能家居 IoT 中枢,可能性几乎无穷无尽。
反汇编器
有时候,能够把你的代码再往下看一层,会带来极大的价值。 例如,在 Apple 平台开发中,我们经常要使用 Apple 提供的、基本上是闭源的库。 当某些行为不符合预期时,通常只能通过 Feedback Assistant 提交问题单,然后等待回复(有时甚至要等上几年!)。 但借助反汇编器,你往往可以窥探其内部,从而理解这些闭源代码实际上是如何工作的。
理解你自己的程序所生成的汇编代码也很有用,尤其是在优化场景下。 只有检查编译后的代码,你才能真正知道自己的代码到底在做什么。 就我个人而言,为了这个目的,我会在 macOS 上使用 Hopper Disassembler。
编译器
编程世界里也有自己的“秘密俱乐部”。如果我们去想那些其产品影响了最多工程师的人,最终一定会想到那些构建了我们最喜爱的编程语言背后编译器的人。 在我自己花时间研究编译器之前,这些程序对我来说就像纯粹的魔法。 但即使只是学习基础知识,那层神秘的迷雾依然不会让你完全领会那些创造出整门编程语言的人究竟有多么了不起。
好消息是,这个主题有大量资料和书籍可供学习。 我最喜欢的两本书是 Engineering a Compiler,以及当然不能不提的经典之作 Dragon Book。 你可以从 LLVM 的 Kaleidoscope 教程开始,深入到自己开发一门编程语言。 在“真实世界”的软件开发中,除非你本身就是编译器工程师,否则这些知识能让你对语言行为和晦涩错误有更深刻的理解。 此外,大多数编译器都是开源的,这也让你有机会为那些规模庞大、影响深远的项目做贡献。 例如,为 Swift 仓库做贡献,能显著提升任何开发者的作品集含金量。
操作系统基础
如果说还有比编译器更复杂的项目,操作系统一定算得上。 作为大部分时间都在电脑上工作的开发者,我们很容易忘记:操作系统其实也只是另一个程序,就像我们自己编写的那些程序一样。 硬件之上那一层庞大的抽象,制造出一种错觉:仿佛每个用户态程序都运行在自己独享的处理器和内存上。 这些抽象足够精确,因此同一份编写并编译过一次的代码,能够可靠地运行在种类繁多的硬件配置上。
《Operating Systems: Three Easy Pieces》这本书是理解操作系统如何工作的绝佳起点。 如果你尤其对 iOS 或 macOS 的底层机制感兴趣,我推荐免费的资源 MacOS X and iOS Internals。 深入理解操作系统如何运作,能弥合硬件与我们编写的代码之间最后的一道鸿沟。 对开发者来说,这意味着你几乎可以完全掌控并理解程序运行时发生的一切。
最后总结
回过头看,我并没有遵循一套严格的计划。 我只是不断学习、不断构建,并追逐自己感兴趣的东西。 这份好奇心最终变成了一份职业。
如果你现在过得很舒适,却在思考接下来该做什么,不妨试着把视野拓宽。 去探索一些新的东西。 你投入在业余项目和学习上的时间会不断复利,而 Junior、Middle、Senior 和 Principal 这些等级之间的差距,也会比你想象中缩小得更快。
总还有更多东西可以学习。 但每做一个项目、每犯一次错误、每理解一个新概念,你都在不断向前。 继续构建。
— Andrii Zinoviev,首席产品工程师
想加入我们的工程团队,并影响数百万人吗? 来看看我们的职位空缺吧!
The Readdle Team