学习极客时间-左耳听风专栏的个人学习记录。

技术基础

技术变现

我想这种感觉,我现在可以说清楚了,这种潜意识就是——我完全没有必要通过打工听人安排而活着,而是反过来通过在公司工作提高自己的技能,让自己可以更为独立和自由地生活。

因而,在工作当中,对于那些没什么技术含量的工作,我基本上就像是在学生时代那样交作业就好了。我想尽一切方法提高交作业的效率,比如,提高代码的重用度,能自动化的就自动化,和需求人员谈需求,简化掉需求,这样我就可以少干一些活了……

这样一来,我就可以有更多的时间,去研究公司内外那些更为核心更有技术含量的技术了。

要去经历大多数人经历不到的,要把学习时间花在那些比较难的地方。

要写文章就要写没有人写过的,或是别人写过,但我能写得更好的。

更重要的是,技术和知识完全是可以变现的。

工作中,应该更接纳那些可以提高个人技术的活(虽然这些会更痛苦),尽量缩短对自己无帮助的工作内容时间(用脚本或其他方式优化,尤其是重复的那些无意义事项)。比如看似无害的功能需求,只是谋生的手段,对自身帮助其实不大。厘清工作的最终目的,应该还是要立足于自身,提高自身的硬实力。在工作不出错的情况下,多向外历炼自己,开拓自己的技能和视野。多写技术文章(注重质量),算是程序员的作品册了。

动手能力很重要。成为一个手艺人,动手能力是很重要的,因为在解决任何一个具体问题的时候,有没有动手能力就成为了关键。这也是我一直在写代码的原因,代码里全是细节,细节是魔鬼,只有了解了细节,你才能提出更好或是更靠谱、可以落地的解决方案。而不是一些笼统和模糊的东西。这太重要了。

会挣钱的人一定是会投资的人。我一直认为,最宝贵的财富并不是钱,而是你的时间,时间比钱更宝贵,因为钱你不用还在那里,而时间你不用就浪费掉了。你把你的时间投资在哪些地方,就意味着你未来会走什么样的路。所以,利用好你的时间,投到一些有意义的地方吧。

数据安全

大公司的数据安全泄露事件 — 往往需要付出巨大的代价,因此需要在源头就做好它,尽可能的组织这类事件的发生

  • 理解你的产品
  • 建立一个快速发布更新的流程
  • 建立多个安全层
  • 异常模式的监控机制
  • 敏感数据只入不出,外部系统用API调用相关功能,而不是直接把敏感数据返回出去;如果是需要在界面上回显的,也要是脱敏的。如果一定要完整数据的话,也要加密返回

技术领导力

  • 野蛮开采
  • 资源整合 - 管理
  • 精耕细作 - 科学
  • 发明创造 - 高科技

会看人类的几次工业革命:

  • 第一次:机器取代人力 - 蒸汽机
  • 第二次:电力和内燃技术 - 电灯、电报、无线电
  • 第三次:信息技术革命(ing)

技术领导力(WHAT):

  • 尊重技术,追求核心基础技术
  • 追逐自动化的高效率的工具和技术
  • 解放生产力,人效的提高
  • 开发抽象和高质量的可重用的技术组件
  • 坚持高于社会主流的技术标准和要求

怎样算是有“技术领导力”的软件工程师:

  • 发现问题
  • 提出解决问题的方案,比较方案之间的优缺点
  • 正确的技术决定
  • 更优雅、更简单解决
  • 提高代码质量(扩展性、重用性、可维护性)
  • 正确方式管理团队
  • 创新能力

如何拥有“技术领导力”(HOW):

  • 扎实的技术基础
    • 基础知识越扎实,才能走得更远
    • 计算机技术有共同之处,一通百通
    • 高维度(如分布式)可以从基础知识中找到影子
    • 主要分两部分:编程和系统
      • 编程:C语言(深入了解计算机运作)、编程范式(比如面向对象编程(C++、Java)、泛型编程(C++、Go、C#)、函数式编程(JavaScript、 Python、Lisp、Haskell、Erlang)等)、算法和数据结构
      • 系统:计算机系统原理、操作系统原理、数据库管理、网络基础、分布式
  • 非同一般的学习能力
    • 高质量的信息源(Google 等搜索引擎,Stack Overflow、Quora 等社区,图书,API 文档,论文和博客等)
    • 举一反三、不怕困难、开放的心态
  • 坚持做正确的事
    • 提高效率、管理时间
    • 自动化操作
    • 掌握前沿技术
    • 知识密集型的事
    • 技术驱动的事
  • 不断提高对自己的要求标准
    • 敏锐的技术嗅觉
    • 强调实践,永远在编程

这么说吧,如果今天使用中文搜索就可以满足你的知识需求,那么你就远远落后于这个时代了。如果用英文搜索才能找到你想要的知识,那么你才能算跟得上这个时代。而如果说有的问题你连用英文搜索都找不到,只能到社区里去找作者或者其他人交流,那么可以说你已真正和时代同频了。

推荐阅读

推荐了程序员应该看的书单和一些很有用的文档链接

Go语言,Docker和新技术

Go语言的杀手级应用是Docker,Docker是现在以及未来的趋势。

作者探讨了新技术能发展起来的几大因素,以及在新技术成熟起来就接触它的好处(体验技术发展的过程,收获会比技术本身高!)

PaaS 是一个被世界或是被产业界严重低估的平台。

其他

时间一定是能找得到的,关键还是看你的渴望程度和热情。只要你真心想把事儿做成,你就一定能想出各种各样的招儿来挤出时间。

  • 20-30 : 打基础的阶段、开拓眼界。(快结束了)
  • 30-40 : 人生发展的阶段,敢想敢干

20-40 : 每个人最黄金的发展阶段

20-30 岁应该多去经历一些有挑战的事,多去选择能给自己带来更多可能性的事。多去选择能让自己成长的事,尤其是能让自己开阔眼界的事情。

很多事情能做到什么程度,实际上在思想的源头就被决定了。

我最近有个感慨就是,很多事情能做到什么程度,其实在思想的源头就被决定了,因为它会绝大程度地受到思考问题的出发点、思维方式、格局观、价值观等因素影响。这些才是最本源的东西,甚至可以定义成思维的“基因”。就我们程序员而言,我认为,编码能力很重要,但是技术视野、技术洞察力,以及我们如何用技术解决问题的能力更为重要。

如何成为一个大家愿意追随的Leader?

技术领导力(leadership) - 技术领导者(leader)

leader需要的素质:

  • 赢得信任
  • 开放心态、有自我倾向性的价值观(对新技术和别人的观点开放性态度,但也明确自己的倾向,不能听风是风听雨是雨)
  • 以身示范
  • 保持热情和冲劲。正视问题和错误
  • 抓住重点,看透本质
  • 为他人创造机会(别人愿意跟随你)

错误处理

探讨了错误码和异常捕捉处理(try catch finally)

需要注意,异常捕捉处理在异步处理的世界行不通

两种错误处理方式,有各自的优缺点和适应场景(然而并没有清晰的界限)

一般来说,异常捕捉处理的代码更为清晰,处理更为完善

将常见异常场景分为三种:

  • 资源错误(环境)
  • 逻辑错误(代码问题)
  • 业务错误(如入参格式错误)

从异常出现的角度:

  • 希望杜绝:代码错误
  • 杜绝不了:用户业务错误
  • 希望可以恢复:环境错误,可用一些重试或降级机制尝试恢复

一个可能的场景分类:

  • 我们并不期望发生的:异常捕捉处理
  • 可能会发生的:错误码

但需要明确的是,有些情况我们也没得选:

在 C++ 重载操作符的情况下,你就很难使用错误返回码,只能抛异常;

异常捕捉只能在同步的情况下使用,在异步模式下,抛异常这事就不行了,需要通过检查子进程退出码或是回调函数来解决;

在分布式的情况下,调用远程服务只能看错误返回码,比如 HTTP 的返回码。

聊聊异步编程的异常处理机制

如上所说,因为异步调用是运行在不同的线程中,所以无法用传统的异常处理方式。主要源于:

  • 无法获取到异步线程的返回错误码
  • 无法catch到异步线程抛出的错误

关于“无法获取到异步线程的返回错误码”的解释: 函数调用瞬间返回的可能只是中间状态标识/占位符(如 Future/Promise 对象),此时返回的占位符本质上只是任务提交成功的回执(类似于饭店给你一张排队小票),并不包含最终的执行结果或错误码。真正的计算结果会在未来的某个时间点产生。因此,在异步编程中,必须建立主动监听结果的思维模式,而不是依赖同步的返回机制。

解决方式:

  1. Promise()
1
2
3
4
5
6
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
}).catch(failureCallback);

Java中可以用CompletableFuture

1
2
3
4
CompletableFuture.supplyAsync(Integer::parseInt) //输入: "ILLEGAL"
           .thenApply(r -> r * 2 * Math.PI)
           .thenApply(s -> "apply>> " + s)
           .exceptionally(ex -> "Error: " + ex.getMessage());
  1. async/await
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
async function foo() {
  try {
    let result = await doSomething();
    let newResult = await doSomethingElse(result);
    let finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch(error) {
    failureCallback(error);
  }
}

如果在函数定义之前使用了 async 关键字,就可以在函数内使用 await。 当在 await 某个 Promise 时,函数暂停执行,直至该 Promise 产生结果,并且暂停不会阻塞主线程。 如果 Promise resolve,则会返回值。 如果 Promise reject,则会抛出拒绝的值。

而我们的异步代码完全可以放在一个 try - catch 语句块内,在有语言支持了以后,我们又可以使用 try - catch 语句块了。

关于这里的暂停等待await执行的过程,我之前有个误解:为什么要这么做,同样的需要等待函数执行结果,这里用await+异步,和传统的同步做法,到底有什么区别?下面的解释: 首先,需要理清楚的是,异步做法不会阻塞主进程,而同步做法会。

同步函数 异步函数(async/await)
主线程 完全阻塞 只在 await 暂停当前函数
浏览器响应 卡死 保持流畅
耗时操作 必须同步完成 可委托给底层系统(如网络线程)

之前误以为异步的暂停和传统同步在代码上效果没有分别,忽视了方法后面还有其他动作的场景,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
async function foo() {
  console.log("Start");
  await fetchData(); // 假设需要 2 秒
  console.log("Data received");
}

foo();
console.log("After calling foo");

// 用户同时点击了页面上的按钮
button.addEventListener("click", () => {
  console.log("Button clicked!");
});

在这个代码示例中,foo() 方法中需要等待fetchData()的执行。但是因为foo();方法调用时整体没有加await,所以下面的代码可以继续执行,无需等待fetchData()的执行。

输出结果:

1
2
3
4
5
Start
After calling foo
(2 秒内用户点击按钮则会出现)
Button clicked!
Data received
  • 即使你的代码没有后续逻辑,浏览器环境仍然有:
    1. 渲染任务(16ms 一次的页面重绘)
    2. 事件监听(点击、滚动、键盘输入)
    3. 其他异步任务(setTimeout、其他网络请求)
    4. Web Worker 通信
    5. 垃圾回收等底层机制

ps. 前面主要提到的是前端代码的异步处理,关于后端如Java的异步处理对比:

维度 前端(JavaScript) 后端(Java)
驱动原因 保持 UI 响应性 提升吞吐量,降低资源消耗
典型场景 网络请求、动画 数据库访问、RPC 调用、文件 IO
语法糖 async/await 原生支持 需要框架支持(如 CompletableFuture、Reactor)
底层机制 事件循环(单线程) 多线程 + 事件驱动(如 Netty)
错误处理 try/catch 包裹 await 回调函数或链式异常处理
并发模型 单线程异步 多线程 + 异步(更复杂资源管理)

错误处理的最佳实践

  • 错误字典,做好错误的分类;错误文档,如RESTful API+Swagger
  • 错误日志输出最好用错误码(不一定是数组,可以是一个清晰的英文短语),而不是具体的错误信息:这样方便监控布置
  • 忽略错误要记日志,否则不好维护
  • 同类错误最好用一样的处理模式,如统一返回null或抛出NullPointerException
  • 尽可能在发生错误的地方处理错误(调用者更简单);要返回错误的话,尽量返回原始错误
  • 处理错误,要记得清理资源
  • 对于异步的方式,推荐使用 Promise 模式处理错误。对于这一点,JavaScript 中有很好的实践。
  • 对于分布式的系统,推荐使用 APM 相关的软件。尤其是使用 Zipkin 这样的服务调用跟踪的分析来关联错误。

魔数 - 太难了 看不懂。。

机器学习101

分类:

  • 监督式学习:根据样本识得规律
    • 缺点:样本数据很重要;噪音数据
  • 非监督式学习:未标注过的数据,自动分类(找到潜在的关系)

找规律 - 数据建模

时间管理:同扭曲时间的事儿抗争

主动管理

不要处于被动的位置,要化被动为主动。向外界明确自己某段时间在专注做事,表示不想被打扰。

学会说“不”

对于自己感觉完不成的任务,说“不”很重要。但是很多时候,我们没法直接说“不”。有以下几个迂回的方式:

  • 不要直接说不,要说思考下。然后给出思考后的结果,比如给出自己能实现的方案,不要直接回绝
  • 对方给出一个时间紧任务重的事情事,给对方三种选项让对方选(主动权反转)
    • 加班做完,但是不保证质量,且给自己一个月时间修补可能的缺陷
    • 保质量的做完部分需求
    • 可以保质量做完所有需求,但是需要多给时间
  • 对方给出一个复杂的需求,评估下来不好实现的话。尝试换个思路,思考下这个需求要解决的根本问题是什么,进而思考有无其他方案解决

加班和开会

这个和自己不是很挂钩,先略

时间管理:如何利用好自己的时间

对于时间管理,提到了三方面:投资、规划、用好

投资自己的时间

  • 基础打牢,初级程序员很多时间是用在差错上,这是因为基础没打好
  • 花时间在解放自己生产力的事上:多做自动化、可配置、可重用、可扩展的东西,用现在的时间节省未来的更多时间
  • 花时间在能让自己成长的事上
  • 构建好适合自己的高效环境,程序员需要有一套自己上手的环境

规划自己的时间

  • 定好todolist的优先级。结合自己之前看到的观点:不应该出现紧急又重要的事情,凡是重要的事情,我们就不应该把它压到紧急
  • 短作业优先:这个给了我一个启发。也就是在相同优先级的任务中,选择比较快能完成的任务。这样可以在开始比较快的划掉一些todo项,成就感+减少焦虑感
  • 想清楚再做。尤其对于不是很有把握的事,要先整体考虑好(尤其考虑可能存在瓶颈或不顺利的部分)。尽量减少返工的可能,返工很浪费时间
  • 关注长期利益规划。有时候,宁愿早起苦点累点,考虑到长期的发展(比如避免后续维护的困难),可以在当下花费时间更多、或多挨点骂。长期成本比短期成本大得多。

用好自己的时间

  • 将军赶路不追兔子。明确自己的主目标,不要被路上的花花草草干扰。
  • 形成习惯。好习惯见过很多,真正属于自己要靠坚持实践以及和自身情况结果改进
  • 形成正反馈:把时间花在有价值的事上,比如解决痛点
  • 反思(复盘)和举一反三

另外,看到评论区的一句挺有趣的话;

他是放羊的,你是砍柴的,你陪他聊了一下午(开会讨论)。他的羊放好了,你的柴呢?

故障处理之应对故障

故障发生时

最重要的就是:尽快恢复->尽快找出故障源

故障的多米诺骨牌:有时候,故障会渐渐波及到其他系统。所以故障解决需要快!

解决故障常见做法:

  • 重启或限流:主要解决的是可用性的问题
  • 回滚:一般是因为代码的bug
  • 降级:如无法回滚,挂一个服务停止服务的公告
  • 紧急更新

故障前可以准备什么

  • 以用户功能为索引的服务和资源的全视图:便于找到故障的路径
  • 准备一套故障恢复的运维步骤、应急手册等
  • 设定故障等级,级别越高,需要牵涉越多人员和处理流程来干预
  • 故障演练

一些大公司,如 Netflix,会有一个叫 Chaos Monkey 的东西,随机地在生产线上乱来。Facebook 也会有一些故障演习,比如,随机关掉线上的一些服务器。

  • 灰度发布

故障处理之故障改进

故障复盘

介绍了亚马逊的故障复盘方法:写COE(Correction of Errors)文档,包括以下内容

  • 故障的整个过程,如几点几分做了什么
  • 故障原因分析
  • 5WHY:反问至少5个WHY
  • 后续整改计划:根据这5个WHY说明后续如何举一反三改进

故障改进

所以,这里给出三条我工作这 20 年总结出来的原则(Principle),供你参考。

  • 举一反三解决当下的故障。为自己赢得更多的时间。
  • 简化复杂、不合理的技术架构、流程和组织。你不可能在一个复杂的环境下根本地解决问题。
  • 全面改善和优化整个系统,包括组织。解决问题的根本方法是改善和调整整体结构。而只有简单优雅的东西才有被改善和优化的可能。

简化掉系统复杂混乱的部分,因为你不可能在这种环境下做的好。

我们应该能够识别的表象和本质

主要讲了这几个观点:

  • 兴趣是可以培养的,真正能使人持久投入精力的是正反馈(成就感)
  • 工作和让人成长不是等价关系,实际上是有足够的实际解决问题的场景、高手讨论帮助的场景才能真正带来学习。如果不是这样的工作环境,反而会让你停止脚步。开源社区的高手更多,如果想进步可以去那里。

另外:

关于技术的选择:人们通常认为技术含量高的技术其价值会更高,而历史上无数的事实却告诉我们,能规模化、低成本、高效率地解决实际问题的技术及其基础技术,才发挥出了更为深远的影响,甚至其价值更是颠覆性的,难以估量。

GIT协同工作流

介绍了几种协同工作流

  • gitflow 协同工作流
  • github/gitlab 协同工作流

关于git push --no-ff:这里的fffast forward “允许快进”的意思。如果指定了--no-ff,则会在被合并分支上有诸如 Merge branch 'feature-a' into release 的提交记录。否则(允许快进的方式),提交历史会是线性的:

1
2
3
4
5
6
7
8
# 合并前
release: A → B → C (指向提交 C)
feature-a: A → B → C → D → E (指向提交 E)

# 合并后(快进)
release: A → B → C → D → E (直接指向提交 E)

其中 A B C 等是commit记录的意思

是否推荐push时允许快进的设置,看具体的开发协作的场景。建议:

  1. 快速定位某个功能何时合并到发布分支 → 使用 Merge commit(–no-ff)。
  2. 保持提交历史简洁 → 使用 Fast-forward,但需依赖 GitLab 的合并请求元数据追溯来源。