0%

终于重新拾起笔记录生活了,先给自己点个赞2333🤪

这是来美国之后第一次记录生活,从八月到现在过的很充实精彩,简单总结下吧:

8月11号的飞机,中途经停东京一小时,算是打卡日本了!趁着那十几分钟买了些日本零食,还有一本美女写真(喂难道这不是日本特产么?然后历时18小时达到波士顿。空中俯瞰波士顿的时候,我发现绿色植被占比很高,大多是平房鲜有高楼区。噢对了波士顿沿海,我喜欢靠海的城市(对,我就是在说你,深圳。随后过安检的时候被拉进小黑屋审查了2333,不过警官们非常和善地盘我基础信息,把手机电脑都收进去。一直盘了一个半小时,手机不给回消息,导致接机的房东阿姨在外愣等了俩小时=。=辛苦她了。Anyway,算是顺利入境这片全新的wonderland,开始人生2.0!

八月在老戴家借住了20多天,他室友非常热情地接待和照顾我,还有狗子Jimmy和我贴贴了大半个月🐶太可爱了这小家伙。住在高级公寓非常省心,周围很安静安全,每天作息规律,早上带Jimmy去Dog Park,让它自己耍然后晨练。白天时间写写区块链项目,临近晚上到处练滑板。刚来几位高中老同学也各种照顾我带我飞,去逛了 Boston的Downtown经典景点、吃了波士顿龙虾🦞、看了Boston的海(冷冰冰的)、跟周老板去海钓(结果晕船吐成💩)第一个月安安稳稳地度过了适应期~我对波士顿也是好感满满,这里安全、人均素质高,大家都很友善,节奏也适中~

九月开学,斗志昂扬地开始研究生的学习。Northeastern是一所非常漂亮的大学,非封闭的校园有着很棒的绿植。尤其喜欢Ruggles车站出来的那片枫树群,秋季慢慢褪成红黄色,特别美。

咱每学期只上两门课,每周四天,每天一节课。虽然课业挺轻松,但通勤就显得非常耗时了啊,特别是开学20天地铁在修,每天来回耗时3小时比上课还久哈哈。7205是算法课,(中国?)女教授是MIT博后,但我觉得教课很一般,现场板书完然后照着课本讲=。= 相比之下,我很喜欢7374计算机网络的Dimi教授,希腊人讲英语带点口音,但是教课非常有热情,讲课很注重学生参与,喜欢这样的启发式教学。

对了,九月份参加了 HackBoston的高校区块链黑客松比赛,坐标Harvard大学。凭借我的idea – Cryptolice 智能合约安全查询产品(基于BNB的安全API)和全队的努力一举斩获BNB Track的第一名🥇认识了来自巴拿马的Isaac,一位Babson College(创业类大学全球第一)的Business大三学生,很成熟很有想法,做得一手漂亮的PPT。反正赏金$2500💰,虽然大部分开发都是我完成的,但最后我决定给大家平分,交个朋友😄

十月份嘛,大部分时间专注在Suiet Wallet的开发上,吃到了Aptos链的红利,团队一举做到了20万的安装量和推特4万粉🔥开始大量与项目方合作对接,写的代码也不再是单机了,开始实现和设计协议,以及开发组件给其他开发者使用。说实话蛮锻炼技术和思想的,特别是后期接入用户多了之后,出任何Bug都很很麻烦,以前写的不合理的代码也都只能切成Deprecated不能删,甚至不能够对币圈开发者有太高的预期2333(比如寄希望于文档去解答问题,结果还是贴脸服务解决问题🌚

十月份课业逐渐难了起来,经历了两门课的期中考试,两门班级的均分都是60+把我惊呆了。都没发挥出最好水平,保持中上游的位置,但是想要稳妥地拿到A,竞争还是蛮激烈的,所以到了十一月就倾向于把更多时间安排到学习上了。

自己对现在的生活挺满意的,每天很规律,学习、做项目、做饭,每周滑板、打球、打打LOL。独处的时间很多却不觉孤独,每天都有满满的充实感。随着明年夏季实习的压力逐渐上来了,接下来要开始准备刷题了!目标是Google和币安!

晚安,Boston!

哟伙计,被我标题党骗进来了吧👻👻

哎哎别走,虽然标题取得很有误导嫌疑,但是我可没撒谎,我真的在听MIT的课哦——就是那所QS全球大学排名连续11年霸榜No.1的麻省理工大学 (Massachusetts Institute of Technology),单项的话如计算机科学的CS Ranking也高居全球第三!

Massachusetts Institute of Technology

看了上期周刊的朋友肯定会说你可拉倒吧,你个东北大学生搁这装💩 好吧我招我招,我听的是由MIT发起的OpenCourseWare平台的在线课程,这是一个拥有超过2,500门课程的免费公开课平台,致力于免费分享最优秀的知识给全世界的学习和教育者!

OpenCourseWare官网

而我最近正在学习MIT CS专业本科的课程,真的都能找到,而且有高清的视频、完整的讲义和课程资料,简直不能再赞了👍🏻

OpenCourseWare-课程列表

这么好的资源我是怎么找到的呢🤔

一开始我只是在想:“我还没接触过全英的课堂耶,会不会我知道的知识换了英文就完全听不懂了🤪” 怎么办?找Native Speaker聊天侃地?Nonono,我要熟悉的是计算机相关的术语和交谈,要找这样的partner有点困难。如果我能知道美国大学的课堂是怎样的就好了?于是带着关键词在网上冲浪,诶,果然有,而且还是世界第一的大学课堂,美滋滋🚀

于是我决定在NEU的研究生课程开始前,先把MIT计算机本科的课程学完。这样一来,一我能提前熟悉全英的课堂,二来对齐了美国大学本科课程的内容,有助于无缝对接我的研究生课程!

所以本期周刊,我想跟大家分享怎么在MIT官网查找自己感兴趣的专业课程大纲,以及如何在OpenCourseWare平台搜索&筛选对应的课程💡

📦 前置准备

✅ 科学合理的上网

(很遗憾,在国内要能接触到全世界优秀的学习资源,这是唯一的前置的条件。如果有不被局限的梦,那就尽你所能去克服这些障碍吧

🔎 查找专业的课程大纲

为什么要找课程大纲?当然是为了模拟成为MIT学生的关键仪式感!(bushi 🤪 其实是遵循他们制定的培养计划来找课,学习将会更成体系更科学~ 就好比旅行前要做行程规划,而不是直接飞到当地再去找景点!

正题开始,我会以我的专业计算机工程 ECE ,找到MIT本科ECE课程大纲为例子来讲解✅

首先,打开你的搜索引擎(以百度为例),输入 MIT + 你的专业英文缩写 (例如 MIT ECE),我们会得到该专业对应的MIT的学院的网址(.mit.edu 结尾),点进去

💡Tips:美国大学一般每个学院都会有自己的网站,要找某专业的课程大纲当然要去对应学院的网站查找

百度搜索-MIT ECE

接着找到 academics(学业)> undergraduate programs (这里找的是本科生的课) > curriculum(课程),点击进入

MIT EECS

MIT EECS - ACADEMICS

Bingo,目标就在我们的左侧菜单栏!我的专业对应 6-3: Computer Science and Engineering ,于是继续点进去:

MIT ECEECS - 6-3 Curriculum

这样我们就找到MIT本科计算机工程专业对应的完整课程培养计划了✅

课程在大纲中会分为基础、进阶方向等主题,其中课程的代码(如6.3400)将是我们搜索课程的关键词 ⚠️

那我们的学习顺序应该是怎样的呢?看到最下面的 Degree Roadmap (课程发展线路)了吗?点击链接后将下载得到一份pdf,里面展示了课程之间的关联以及每学期该学习哪些课!能够感受到世界一流大学教务的贴心:

Roadmap-1

Roadmap-2

📖搜索&筛选在线课程

拿到了课程大纲和课程代码,我们就可以在 OpenCourseWare 平台上搜索课程咯😎

浏览器输入 https://ocw.mit.edu/ 打开平台,以 6.042 - Mathematics for Computer Science 为例,输入代码+搜索🔎

OpenCourseWare

OpenCourseWare-courses

Bingo,我们搜索到几个选项,都是一门课,不同的老师讲解的选项。

这里就要讲下筛选课程的注意事项,我们要选择有教学视频、讲义、作业的课程为佳✅有的课程仅有文字资料就不是太优的选择❌比如下面这个,关注 LEARNING RESOURCE TYPES区块可以得知本门课是有视频、例子和作业的😁

OpenCourseWare-Syllabus

点击左侧 VIDEO LECTURES 就能看到完整的课程列表了:

OpenCourseWare-Video Lectures

OpenCourseWare-Video Lecture

课程视频是托管在 Youtube 油管上的(国外视频网站,这正是需要科学合理上网的原因),每段视频非常贴心地配备了英文字幕,不用畏惧出现专有名词啦!怎么样,还不赶快准备好笔记本开始听课?!✍🏻

学而不思则罔,思而不学则怠。当然别忘了课后做练习,点击左侧的 ASSIGNMENTS 开始刷题!✍🏻✍🏻

OpenCourseWare-Assignments

🤔学习的感受

分享到这就结束了,说点近期在平台学习的感受吧💡

1️⃣ 首先衷心地感谢MIT提供这么棒的学习资源和平台,他们肩负着顶级教育者的使命,栽下供全世界求知者汲取知识的果树🍎 加上如今互联网的普及,没钱学习不再是桎梏,读大学也未必需要去大学,只要你想!

2️⃣其次真正开始听课后会打破担心听不懂的顾虑,有了语言关托福/雅思打底,不带字幕听下来是完全没问题的,作为磨耳朵材料再适合不过了。顶多电脑开着有道词典,遇到屏幕里不懂的词就查嘛。

3️⃣最后就是我看的几门课的老师都博识而风趣,他们讲课是站在讲台上跟学生在互动(而不是我大学印象里坐着念ppt

说到PPT,内容材料的选型也非常的新颖活泼:比如Python数据处理的课讲递归那门课,教授拿出玩具汉诺塔,像变魔术一样移动盘子,完成后等待掌声的样子太有意思了😆

讲课的方式也是生动有趣,比如离散数学的那门课教授解释推断 implification的概念 A implification p=>q is True if p is False or q is True 时举例:猪会飞=>我是国王 这个推断是正确的,因为猪会飞是假命题,前提不成立所以后面的命题无论真假,该推断都认为是正确的(我理解为前提不对的话那你随便推断都可以)。就像这样,教授用风趣的例子解释了有点难以理解的定义,一扫传统数学课的枯燥🥸

综上,通过OpenCourseWare平台,我有机会感受到顶级学府生动的课堂、跟用心讲课的教授隔空面对面,感觉很棒🥳

✨本期推荐

📚 MIT OpenCourseWare

本期推荐就是 MIT OpenCourseWare 公开课平台啦,链接是:https://ocw.mit.edu/ 🔗

祝大家学习愉快,咱们下期再见👋

BTW:5月30号有几位小伙伴私信我公众号(布鲁斯基星人),我忘了看结果超过十天就没法回复了🥲 希望提问的小伙伴可以再发条消息让我回复,斯密马赛😇

Hi 朋友们!下了一周雨的广东终于迎来一个晴朗的周末,舒服了😌

本期周刊打算分享我本人在2021年10月初开始起意,边工作边着手美国研究生的申请,12月中旬递交所有申请,最终在4月初收到梦校Offer的经历📚 目的是整理这段经历,跟大家坐下来分享一份属于打工人成功学术上岸的案例✅ 我的背景和经历也许并不普适,但如果其中有环节的某些信息能给到你帮助,那我会非常开心👾

那么,正题开始!🛫

背景&申请结果

本科:华中科技大学/软件学院/数字媒体技术专业

  • GPA: 3.4/4.0, 50%
  • TOEFL: 99 (R26/L27/S21/W25) (差一分你说气不气
  • GRE:无

工作:共2.5年

  • 腾讯(深圳),运营开发工程师,2019.07~2020.10

  • 腾讯音乐(深圳),Web前端开发工程师,2020.10~2022.03


2022Fall|我一共申请了4所美国大学,5个专业,分别是:

  • ❌ 彩票 CMU@MSE [2021.12.06投递, 2022.03.16拒绝]
  • ❌ 重点 UCLA@MSCS [2021.12.10投递, 2022.04.07拒绝]
  • ❌ 重点 UCSD@MSCS [2021.12.13投递, 2022.03.30拒绝]
  • ❌ 保底 NEU@MSCS [2021.12.14投递, 2022.02.12拒绝]
  • ✅ 重点 NEU@MSECE [2021.02.14投递, 2022.04.01发offer]

申请记录文档

可以看到申请结果还是相当惨烈的🥲一直被拒到最后,幸好NEU@MSCS邮件没完全拒,给我再申ECE的机会,这才幸运地绝地求生拿到offer(今年真的卷烂了,之前defer的、因为疫情放缓决定出去的汇聚在今年,每所学校拒信都提到今年申请人数爆满💥 所以本文标题才说“居然”做到了,没有标题党吧233

如果对申请表格有兴趣,关注公众号“布鲁斯基星人”后台回复 留学申请表格 即可获取模板!这里要感谢优秀的@小叶同学,我是在她基础上改的~

开篇:年轻就是要敢想、敢做

我有个自认为不好的毛病,就是喜欢把脑子里未成型的想法挂在嘴边,100%被动给自己立Flag,让人觉得轻浮。但后来我觉得这不见得是坏事,至少只有敢想,才可能敢做。

高中开始思考未来去处,“去外面看看”就一直萦绕脑中,而美国对于当时的我来说就是“国际化”的代名词,自然也就成为我最想去探索的地方。于是每次跟人聊到未来时,我一定会说“我想去美国读书”。而实际上我没有去思考去美国干啥以及规划实现的步骤,导致这个牛一直吹到大学毕业、工作两年都还没动静🤪

有时候会问自己,我为什么会在这方面偷懒呢?或者说我在害怕什么呢?

高昂的留学费肯定是第一顾虑

计算机方向两年至少60万+的机会成本对我的家庭来说肯定是一笔昂贵的开销。这相当于一项家庭对自己的巨额投资,如果没有能赚回成本的预期,我不好轻易跟家里开口。

这个问题一直是阻碍我行动的最大因素,我曾经的打算是工作几年赚够学费然后再出去。直到我把工资收入、留学花销列出来后,会发现很残酷的事实:凭我本科水平的能力+国内互联网的薪资+深圳的生活开销,打几年工根本赚不够学费。

但如果把美国同级工程师薪酬加入比较行列,会发现,woc,赚美元去抵美元才是唯一的解呀😇 没有调查就没有发言权,赴美工作的高收入预期给了我行动最基础的底气。

其次就是读研究生对于我的价值是什么?

为了探究这个命题,我花了数个夜晚通过思维导图跟自己对话,并且向各类前辈朋友(美国在读、毕业生,毕业后IT从业者,公司领导)请教,想从全方位去评估这件事的价值,结果如下:

人生选择思维导图

对于职业规划,我把自己感兴趣的方向都写了下来,一一去查资料问从业者记录机遇与挑战。同时考虑到国内大厂工作的体验,我会更注重未来职业的work-life balance。最终在当下阶段得出的最优解是:成为独立工作者、互联网全栈开发者,从事Web3(区块链)、软件开发、网络安全。那么去美国留学于已经工作的我来说,有几个关键价值:

  • Reinforcement 能力补强:前端开发只是工程中的一环,侧重点在打造产品界面、交互上;但要成为全栈独立工作者,我在服务端、数据处理、安全、网络等领域能力不够,我希望给自己机会去学习这些领域的知识,进而完成自身能力的补强。
  • Efficiency 效率:工作后果然会开始向往校园生活啊(笑 大片可支配的时间加上学习的氛围,我觉得是高效学习的充分条件。这点是边工作边学很难有的条件,特别受制于国内互联网的令人疲惫的工作节奏以及工作和生活混搅的日常。
  • Diversity 多样性:在全新的环境,接触世界各地的人,接受不同于中式的教育,可以转换新的赛道,迎接全新的挑战和机遇。

解决了最核心的动机问题,还缺引爆小宇宙引线的那撮火焰🔥 这不,好友realnumber在2021年9月的一条微信消息就来了,没有她这条微信,也许就没有后面的故事了:

引爆点1

引爆点2

因为疫情原因,美国大多数大学不再强制要求GRE分数(Graduate Record Examinations,美国研究生入学考试)!申请美国学校最麻烦的就是要备考语言和GRE,而现在免掉GRE至少省掉2~3个月的准备时间,等于是商场甩卖折上折,股市跌穿250日均线,此时不搏何时搏?!我上官网确认后确实如此,那时我就知道,It’s TIME😎

找中介

等我兴奋后冷静下来,我突然惊觉,现在是2021年10月3日,距离名校申请DDL(12月15日)仅剩两个月的时间!!而且我还有工作要做,年底也是要冲绩效的时候,这波不好弄啊!怎么办,怎么办?

我第一时间想到找中介,经调查,申请美国留学的中介费平均在6~8万左右,这也是笔不小的开销咧。而且问了几位通过中介出去的好友,他们都觉得性价比很低,实际上赚的都是信息差,真正花时间的只有文书部分。

于是我参考前辈们提供的留学经历,列出了今年申请美国学校的环节,看看到底哪些我能做,哪些交给中介:

  • 定位选学校
  • 准备语言考试(TOEFL)
  • 准备申请文书(CV、个人陈述)
  • 联系导师或公司领导,准备推荐信
  • 去学校官网递交申请和材料,缴费
  • 成绩送分
  • 监督推荐人上传推荐信

其实也没那么多要做的嘛,耗时间的就是文书+推荐信准备~于是我找中介的目的就很清晰了:半DIY,只帮我搞定文书的准备,其他自己来。最后对比了几家后,确定了一家老爸朋友开的留学机构,费用也的确是比均价少一半多。(涉及机构名字的一律隐去,有需要后台留言吧)

BTW,后来发现网申这一步也很花时间,填一所用了我一晚上。。。后面还是交给中介来帮我做了

选校,思考职业规划

选校的话题在一亩三分地论坛有很多优秀的帖子,不多赘述,这里只分享我的选校标准:

  • 最低GPA要求小于等于3.4
  • 不要求GRE
  • US Top 50
  • CS Ranking排名高
  • 开设专业 computer science或engineering
  • 所在地区比较安全
  • 最多4所,有梯度(需满足彩票x1、重点x2、保底x1)

结果如下:

我的选校

备考TOEFL(Test of English as a Foreign Language)

Tips: 要考多少分就看所选目标院校的最高、最低准入线要求,努力冲过最高要求吧!

我的时间紧张,从10.3开始学,最多安排3次TOEFL考试,所以时间点定在:10.23 | 11.06 | 11.28

短期提分只能求助于教学机构,在中介的推荐下安排了一家,开始了我紧张而热血的备考生活。

首先,目标可视化!每天睁开眼看到的就是我的目标:

托福目标可视化

其次,严格执行、规律作息!从10月7号放假回去开始,

  • 工作日:每天6点起床背单词刷听力,9点去上班;中午速度解决午饭后,先在楼下练口语,然后找个安静的角落刷阅读;晚上7点准时下班,路上听听力,回去写作到11点,11点30准时睡觉。

  • 周末:All in 教学机构,安排课堂、写作业以及模考。

就这么咬着牙坚持了一个多月,风雨无阻,期间顶着工作onCall的压力,我完成了从首次模考70分到最终99分的飞跃🚀

模考成绩

最终成绩

这应该是我学习生涯中最热血的一次了哈哈哈。这里要感谢我的家人、女朋友给我的理解和支持,也要感谢我的老师们。我能做到,相信大家也可以的。

这里提示一句:我录取的大学标注的TOEFL底线其实是100分,但其官网也说了不是锁死的,左右浮动几分也没什么问题~鉴于我是没时间再安排考试了,所以最终止步99分就提交了

准备文书、推荐信

这块需要的材料是:

  • 个人简历 CV/Resume:可以用超级简历网站制作(https://www.wondercv.com/)
  • 个人陈述 Personal Statement: 主要介绍学术背景、科研/工作经历,申请动机和未来学习规划。需根据每个学校的官网要求去调整!
  • 推荐信 Reference Letter:由学校老师/工作领导提供的关于学生学习/工作表现的评价,以及推荐理由。同样需要根据不同学校要求来微调内容。

个人简历的话我看到有两种风格的内容组织,一是学术向,再就是工作向,分别贴个模板供大家参考:

学术向简历

学术向简历

工作向简历(就拿我自己的吧哈哈)

工作向简历

推荐信部分,由于大部分老师/领导工作都忙,基本没什么时间会帮你写英文推荐信,所以我的推荐信流程是:

  1. 先联系推荐人确认是否愿意帮忙,收集联系邮箱(最好是学校/公司邮箱)
  2. 自己起草中文版的推荐信内容,发送给老师确认、修改
  3. 让中介来翻译为英文版,根据申请各学校的要求来做调整
  4. 让中介帮忙网申,填入推荐人联系邮箱
  5. 提交成功后推荐人邮箱会收到学校的邀请链接,让他们转发给自己,自己再转给中介
  6. 让中介完成推荐信的提交

推荐信还是挺花时间的,过程中推荐人可能会出现忘记转发、邮件跑到垃圾箱等情况,需要我们耐心礼貌地联系推进。在这里非常感谢我的教授们还有公司领导愿意给我推荐,助我逐梦!

静待花开

所有网申都已经提交后,已经到了12月中旬了,接下来就进入漫长的等待期。提交得早的话,二月份就会开始出结果,三四月会迎来offer高峰,五月收到offer也不稀奇。

那么这段时间要干啥?对于我而言,我既然已经迈出申请的这一步,就一定要坚定地走下去。这次申请弄得匆忙,托福没有刷高、GRE没考,还只申了4所院校,心里实在没谱。录取or全拒德?黄色的树林里分出两条路。

为了迎接最好的情况以及预防最坏的情况出现,我决定年后离职,回家休息。最坏的情况无非就是全部拒绝,那就再全力准备一年;如果有学校录取了那就太棒了,可以从互联网陀飞轮中解脱出来,腾出半年的时间好好修养身体、陪陪家人、出去旅游、预习课程。从年龄上看也是最合适的时候,25岁出去,2年硕士+3年工作,争取在而立之年回国,开辟事业。

所以我鼓起勇气,做出了迄今为止最重要的选择,并写下了这篇引发身边很多互联网朋友共鸣的推文:《暂别啦!鹅厂&TME》

葡萄成熟时

从二月份开始一直收到拒信,心里非常忐忑,拒信都提到一个点:**今年的申请人数实在太多了 **

好在Northeastern University东北大学在拒绝我CS专业的同时,给了我一线希望:ECE计算机工程专业对我的简历感兴趣,允许我免申请费再次申请!当时已是2月13号,我仿佛抓住救命稻草一般递出了申请。最终在4月1号(没错就是愚人节🤣,我收到了NEU ECE学院的Offer!!

NEU ECE Offer

如果说要总结申请经验,那么我会建议没有很强学术背景,同时期望未来赴美找工作的“打工人”们直接申请ECE专业吧。一来录取的门槛相对较低(名额也多),二来工作经验在这是很大的加分项,三来课程非常实用利于找工。贴一下NEU今年各专业的录取指标:

NEU2022年录取统计

(看!College of Engineering今年录取的GPA中位值3.4,托福均分100,简直完美匹配我的3.41GPA+99TOEFL背景 23333

这里顺便介绍一下我的大学–Northeastern University 东北大学,俗称“计算机找工神校”。2021年虽说全美排名49不高,但是其CS Ranking排名全球12名,计算机专业实力还是相当可以。最最有名的是它的 COOP (Cooperative Education)项目,直接对接互联网大厂的实习工作岗位,提供4~6个月的工作机会(注意,还是要凭本事争取的哈哈),这对于想要在美国找工作的同学无疑是履历大加分,大厂直通车;同时其CSA(Computer Science Align)项目也是被想要转码的同学们所追捧。对于工作向的我来说,无疑是最合适的选择啦!

总结

曾经遥不可及的美国留学梦,经过脑子一热地说干就干、数个夜晚的自我对话与未来规划、朝6晚12的TOEFL攻坚战、耐心推进的文书与推荐信、接踵而至的拒信,最后收到一封读秒绝杀的录取通知书,我终于如愿以偿,而这一切就发生在短短几个月。

我始终认为年轻就应该去闯荡,不被世俗意义的成功或失败所定义。觉得世界大就去看看,觉得累了那就停下来歇会儿,觉得生活没意思那就去寻找意义,这里我想引用乔老爷子的名言:

If you haven’t found it yet, keep looking. Don’t settle. As with all matters of the heart, you’ll know when you find it. And, like any great relationship, it just gets better and better as the years roll on.

远赴他国求学只是我人生中的一小步,但也是坚定而踏实的一小步⛳️

故事就分享到这儿,祝愿同样逐梦的大家精诚所至,Offer为开✨

本期篇幅写太长,没时间推荐啦!下期再见吧咕咕咕🐦

Hi,朋友们!我是布鲁斯基👾

距离上次发文章已经过去快两个月,真就大懒鸽了属于是🐦 在家里的这两个月,对我来说时间过得不算很快,因为这期间我感受、思考、经历了许多,数次有话想讲,又因为各种原因提笔放下(我承认懒惰占九成🤪

而今天,我终于又燃起动力写文章了!这次想聊下“个人周刊”,一种很适合个人”短平快“输出的内容形式

为什么想写周刊

最近看到Airing熊哥的《周刊(第1期):开刊,为什么写周刊》开刊(Airing是一位我非常仰慕的前同事,主修哲学但技术生涯十分开挂),就去仔细了解了下“周刊” 这种输出形式。我发现同样是定期输出,周刊区别于博客很重要的点在于写作时的心智负担小,以及时间成本低。其中他的周刊开刊词提到:

写博客是我喜欢的「输出」方式,但是我想要另一种更轻量、更频繁地方法输出自己的文字来表达自我,就像我 2014-2017 年那时候的老博客一样,满是随笔、无需雕琢。

因为自己工作忙起来之后,实在是抽不出连续的、大段的时间去沉静下来写文章。

上周我在博客中偶然发现,自己有个读者的网站里有一个 周刊专栏,还发现有一些同学的 周刊 同时也兼顾了一定的深度和思考,我非常喜欢定期输出与分享的这种形式。

同时,顺着熊哥的文章又看到另一位博客大佬 4ark 的周刊《我为什么要写周刊》:

我发现,大部分文章读完就忘了,能记下来的往往只有少部分,这是因为我没有针对文章的内容做总结,也就是用自己的话去输出这些文章的核心内容、以及一些更深入的思考,以此加深自己的理解。

(记笔记)这样「单机」玩,很难坚持下去,所以就索性把这个 Weekly 挂在 Blog 上,虽然也是基本没人看:)但这意义就不一样了呀!无论是对保持更新频率、提高博客逼格都能提到一些帮助(不是吗?)。

希望我这个周刊既有专业性的内容、也有我个人的一些思考在里面,毕竟有一些琐碎的事情不想专门开一篇文章来长篇大论,就特别适合写在我的周刊里面。

我看完be like “我看不懂,但我大为震撼”.gif,但“个人周刊”这个新概念duang地印在了我的脑子里,这的确是一种可以有主题但无需很深度的分享方式,既可以记录自己的思考与碎碎念,还能与外界建立沟通的渠道。就像4ark所说,主要读者想着是自己就行了,这样写东西就没啥负担和压力。

写什么内容

记得我的好友富可敌国晋先生说过一句名言,”分享就是要有利他性“。作为对外发布的周刊,如果只是作为自己的日记本,既没意思也不合适,而且咱也不能单纯码一堆链接文字然后不加咀嚼就发出来是吧。

熊哥的周刊里提到的两个方向:”主题思考“与“推荐分享”,与我听过的web3主题的播客《fork it》内容组织形式很像,他每期节目都会拟一个主题,邀请一位有实力的从业嘉宾来做座谈,最后每人安利一样东西。整个节目听下来除了从有深度的谈话学到东西外,也会偶尔被有意思的分享安利到。所以我的周刊也会按照这样的结构去组织:

  • 主题分享:挑一个有意思的主题,做些资料收集,笔记记录与个人见解的分享!
  • 安利推荐:把近期生活里看到的、用到的、学到的好东西安利给大家!

开刊日

见贤思齐,我决定于今天(2022年5月22日)开始自己的周刊,尝试更频繁地输出以及与朋友们分享交流!

我相信打开紧闭的窗户,让新鲜空气流通,就能感受到不一样的和风拂面🍀

本期安利

本期主题是个人周刊,那咱就分享一个能够订阅他人文章发布的工具,叫RSS阅读器——NetNewsWire 📖

NetNewWire

这是一个免费、开源、专为Mac, iPhone, iPad打造的RSS阅读器,可以帮你盯着你喜欢的作者们,一旦他们发布了文章,你就可以及时地在这款阅读器中接收、翻阅与标注归档。

为什么要推荐RSS阅读器?因为它让你可以在杂乱无章的互联网世界,主动地去订阅你感兴趣的信息源,或者一键导入/导出收集好的信息源,极大地提升你收集信息的效率!可以说不会用RSS阅读器,上网冲浪的乐趣都要少掉一大半!

做个类比的话,你喜欢看报纸📰,不用RSS阅读器你就得跑到外面一份份地买报纸回来看;而用了RSS阅读器就相当于有个邮箱📮,每天都有送报纸的给你把各家报纸送到家门口,你只需要每天早上取出来,惬意地边喝咖啡边看报。

顺带一提,所谓RSS (Really Simple Syndication)是一种网络信息包装和投递的协议。有了这个协议,每个人都可以成为信息提供者,每次发布新内容时就能及时告诉大家:“嘿,我的老伙计,我有新文章咯!” 它就像一个网站的海报,里面包括这个网站的最新内容,会自动更新。具体可以参考阮一峰老师的:《如何使用RSS》

具体使用其实非常简单,我们只需确认别人的网站提供了RSS链接:

阮一峰的博客

胡涂说的博客

然后把链接复制下来,导入阅读器:

这样一旦作者发了新文章,我们就及时拉取到啦:

最后再附一些不错的个人周刊链接吧!如果有朋友也写了些周刊的想法,不妨看下先行者们是怎么做滴✨

本期周刊到此结束咯,咱们下周再见 ; )

在提交完最后一次整理项目REAMDE的merge request之后,我合上了Macbook——我的老战友,跟我的公仔们还有工卡放在一起,咔嚓——来了张合影。

没错,我的鹅厂生涯暂时要告一段落啦~


说起离职,我其实有过好几次冲动,每次的理由都不一样哈哈,然后又会听取朋友或前辈们的意见,冷静下来。但这一次,我做出了决定,如果要总结原因,我觉得是到“时候”了。

这里的“时候”,可以理解为三层意思:

  1. 是圆我的留学梦的好时候了
  2. 是时候重视生活与工作的平衡了
  3. 是时候想清楚我想成为什么样的人了

我想“行万里路,读万卷书”

去年9月的时候,朋友微信上突然跟我说:“美国Master今年申请不要求GRE哎!”

我查了下几所知名的大学,GRE确实是Optional了??这一消息把原本打算22年(工作满3年嘛)才开始准备留学的我,提前赶上了跑道。因为早就耳闻GRE挺麻烦的,免去了GRE相当于抄底申请呀哈哈。

我以为我英语还不错啊,结果第一次托福摸底考了个70分…于是在10月份的时候,我报了下线1对1托福班,开始了早六晚十二的边工作边考语言的生活(早上六点半爬起来背单词,中午饭后刷口语,午休刷阅读,晚上7点下班刷写作TUT)。还在墙上列了排期表,每过一天就叉掉,不断给自己正反馈。

经过了2个月的超自律的突击,我赶在11月底考出了99分(就差1分到100,真是气死人)…

嘛,看了下官网好像不是强制100分,那就这样吧,赶紧搞文书去,最终在名校DDL12月中旬前,申请了4所美国(CMU@MSCS, UCLA@MSCS, UCSD@MSCS, NEU@MSCS)、2所香港(HKU@MSC, CUHK@MSC)、1所新加坡(NTU@MSAI)。

出国留学这事儿,对我来说学历镀金可能只是个结果,我更在意的是出去看看、静心学习的过程。 我想趁还没有到被年龄、婚姻等束缚的年纪,满足我探索这个世界的好奇心。同时我相信大部分学生出来工作后,都会想念大学时大片的可支配时间吧!只可惜本科时玩心太重,没有把时间用好,现在带着目的重回校园,希望能不负韶华,学到真材实料。

至于为什么是美国,当然也是因为那边能看到、体验“全球化”的缩影,开阔视野,求同存异,广交人脉;也因为美国是计算机的发源地,去源头学习CS与工作,肯定会有不一样的收获吧。疫情和安全?在跟几个已经在美国的朋友聊过之后,其实做好个人防护和选相对安全的城市生活,还是有应对和避免的策略的。

所以现在有“行万里路,读万卷书”的机会,我当然愿意去争取!(保佑我三四月份能收到一份offer吧

我想要健康、能陪伴家人、完整的日常

2019年毕业体检,我就检查出有胆囊息肉(6x4mm),到了2021年7~12月分别测了两次,胆囊息肉从8mm长到10mm,长得太快了!医生说超过10mm就要切除胆囊了,对,是切除胆囊不是息肉,主要是怕胆囊病变。

胆囊息肉的成因就是油腻、饮酒、熬夜与压力。大学时比较浪,喝酒烧烤熬夜啥的;工作之后,我在熬夜和饮酒都有所克制,剩下的就是压力了。虽说我在的部门有双休(大约985的工作节奏),工作日能够按时完成业务就不错了,然而还有绩效和个人成长会一直萦绕心头,让我在休息时还会想要抓紧学技术。离谱的时候周末都会掐着表规划和学习,很多时候甚至会忽略家人和女朋友的感受,自己给自己压力太大,把自己整得焦虑不堪。

还有一件事让我压力倍增。去年8月,前leader拉我垫背二星。我上半年接了他甩来的联合项目锅,摊子蛮烂的说实话,人力少又服务多个业务部门,已经认真尽心尽力地在做,时间都耗上面了。结果因为换组该项目不是组里的目标了,他拿我没有技术输出来说事儿,硬给二星。一那个项目就是技术中台,提升了业务运营效率,到他那就不算技术输出;二是组内的技术项目我也有在参与和设计,他老早不关心代码了就选择忽视说没看到呗。真是受不了这侮辱,直接开怼+要走。虽然我后面还是冷静下来换组了,但对我后续的工作无形增加了很大的压力,毕竟拿个二星,年终少了不说,晋级要比正常人晚2个周期,哎。

槽吐完了,回到身体健康的话题来。我妈心软,看我压力大,常常劝我说“人生很长,做任何事都不用急这一会儿,但身体健康是关乎一辈子的”。大厂本来就注定是紧张快节奏的,加上自己的精神和身体状态都不太好,所以我决定Slow it down,让自己喘口气,调整身体和心态,再出发。

BTW,去年10月份突如其来的车祸夺走了我的外婆,也打碎了我美满的家庭。外婆勤快,烹饪家务样样拿手,有外婆在的家,是温馨而井井有条的,每次回家都能看见她慈祥开心的笑容,吃到她特意准备的饭菜。她的离去真的给我们打击很大,尤其是我的母亲。我想大学之后就没好好在家待过了,如果我22年拿到offer,8月就会前往异国,又要离家很久。我想多陪陪家人,外婆不在了,家里就由我来打理吧。

暂时告别看不见日落的互联网打工生涯,呼吸、生活才是人生的常态啊。

我想成为Web3.0的独立工作者、创业者

想起21年底游戏大神轻生的事件,有条知乎高赞回答用故事的方式尝试表现互联网人的承压工作现状:远大的理想与骨感的现实,超强的能力与过载的期望,百万年薪与高强度的工作,成功的事业与垮掉的身体,这些对立的矛盾真的刺痛到我的神经。

我到底想要什么,想成为怎样的人呢?在我已经过去2轮本命年的时候,我想我应该有一个大致的答案了。花了几个晚上,在画布上用脑图列出所有我能想到的方向,自问自答,最终没有得出具体的结论,但是承认了一个事实:“我只是一个普通人,我能选择从事感兴趣、有价值的领域就够了”。

Web3.0,这一2021年兴起的区块链概念,阐述了信息即资产的新一代互联网设想。依托于区块链与密码学技术,每个网络公民将对个人信息或生产的信息有着更强的自主权,同时享有收益的权利,这也就是信息即资产的意思。同时加密货币、NFT、DAO、DeFi、GameFi等生态正在吸引全世界的弄潮儿们加入,探索巨头垄断、红利逐渐枯竭的Web2.0时代的新解决方案。将制度交给数学与程序来实现,探索无人区,这对于技术人员来说,是最好的时代;对于非富二代官二代的年轻人来说,也是世界洗牌、阶级跃迁的好机会。

Web3.0的建设者来自全世界,自然不可能都在一处办公,所以成为独立工作者的想法就冒出来了。成为独立开发,意味着我可以选择方向、项目,意味着我可以把控work life balance;当然也意味着收入不稳定、需要超强自制力等等要求。但总体来说,它很吸引我,也天然适合程序员这个职业。

再仔细想想,其实独立工作不意味着单打独斗呀,我还可以找到志同道合的伙伴,一起合作作战,甚至日后创立属于我们的项目和事业。

另外对于加密货币的世界,无疑也是黑客们的黄金时代,网络安全和区块链安全将是hacker们激战的领域,这也是有意思的领域~

其实到了这里,我的视野和能力已经限制了我的想象,产生了很多不确定的不安全感。我肯定想的太简单了,日后的坑让未来的自己踩去吧哈哈哈。不过相比过那种能一眼望到头的生活,我更向往航行在颠簸的大海上去闯荡~

So,All in Web3, LFG!

最后

鹅厂真的就是一所社会大学,在这儿我不仅提高了技术、开阔了视野,还直面了来自企业、社会、生存等质问与挑战。一路磕磕绊绊,也幸有良师解惑、益友相伴,我真的很满意这段旅程!只是我给自己的规划到时间了,得提前下车啦。感恩在鹅厂和TME遇见的各位~

最后引用汪国真的《热爱生命》与大家共勉:

“我不去想是否能够成功。既然选择了远方,便只顾风雨兼程”

一年之计在于春,适合播种,做些新的尝试,开启人生 Round2

2022 年的春节在湖南的最后一天,迎来了一场大雪。真好,瑞雪兆丰年,既是结束,也是开始。

新的一年,我的关键词是 专注 。开始做减法,戒骄戒躁。思考自己想要的生活,大胆地做出选择,并且坚定地执行。

我想要的生活,应该是能够平衡工作与生活的独立工作者,从事有价值的事业,有时间阅读写作,日常有着些许爱好,陪伴家人与宠物,时不时出去旅游看看这个世界。 就足够了,无需大富大贵,强到没边,我只是个普通人,开始认同这一点的时候也长舒一口气。

2022,勇敢地提出辞职,同时有幸遇到 Web3.0 时代以及志同道合的伙伴们,投身 Web3.0 时代的建设,拥抱开源社区

2022 年度的目标写在这里,给自己这一年找到努力的方向:

学业

  • 复习计算机与数学基础,刷算法题,夯实学校与公司的面试要求,拿到美国大学的 offer
  • 提升英语,考出 GRE(320+)

职业

  • 与团队一起投入区块链与 Web3.0 的学习与建设
  • 学好称手兵器,编程语言以 Python、JS 为主,学习 Go, Solidity, C++。

生活

  • 规律生活,放慢节奏
  • 维持住胆囊息肉大小,实在要切除也要调养好身体
  • 坚持健身,增重至 70kg

其他

  • 培养阅读习惯,每月至少读 1 本书
  • 培养写作习惯,创立个人学习分享 IP,每周输出一篇文章或视频
  • 学习书法,每周坚持练习至少 1 次
  • 出一趟远门旅游,看看中国大好河山(新疆、西藏、甘肃选一)

回顾焦虑奔跑的 2021,我终于鼓起勇气去打破安稳的预期,追求自己未完成的出国学业梦,边工作边学托福,朝 6 晚 12 的日子想起来真的自律而热血。

然而自己的身体却开始报警,胆囊息肉已经接近临床判定要切除的大小,这让奔跑的我直接来了个急刹车。我陷入“担心工作没产出”、“不满没有个人生活”与“身体越来越多毛病”的三个焦虑构成的死循环里。

工作到底为了什么呢?思来想去,核心驱动力是赚钱,大厂平台和视野当然是很有诱惑力的驱动因素。但是我在大厂工作几年后有如下不适:

  • 业务需求与绩效压在头上,晚饭后周围同事们不愿过早下班的奇妙现象,企业微信周末也不敢不开。
  • 螺丝钉粒度的工作岗位,如果没有很确定这就是一辈子想做的领域,日子久了会感觉到束缚,要切换赛道也很有成本。
  • 工作日都是早起晚归,休息日也想着要多学些工作技能,看的书也都是技术相关的,其他方面思考与进步为零,除了工作没有其他生活的苍白感。
  • 下班后完全不想写代码,也没啥兴趣了解外面日新月异的技术浪潮。
  • 没有长假期,有钱却没时间去旅游、陪家人了。

Man, I don’t like this way. I want to live and breathe.

到底是我能力问题,还是国内互联网行业的快节奏陀飞轮,反正我找不到 work life balance 的平衡点。既然已经申请了 master,那 2022 年干脆就辞职,gap 一段时间休息一下,等 offer 的同时把失衡的身心和生活调整回来吧!

距离上一次写博客已经阔别两个多月,今天我又回来了。

这两个月发生了几件足以改变我的人生轨迹的事情,第一件是外婆受车祸离世,第二件是我工作之余冲托福一个半月冲到 99 分,第三件就是学校的申请。

10 月 29 日,一个再正常不过的周五 w 晚上,我跟同事刚吃完免费的稻香,准备买单回工位

本篇文章阅读时间:10min
读者预期的收获是:

  • 认识测试驱动开发
  • 非常简单开启你的 TDD 之旅
  • 可以编写自动化测试
  • 重构、重新设计旧的代码更加自信

引子

(压抑背景音乐渐入——)

旁白:为何深夜的办公室传来程序员的哀嚎?

为何说好的一刀 999,砍下去伤害为 0?

为何程序员好基友反目成仇,因代码调用出问题后甩锅大打出手?

当个程序员,好难!(捂着铮亮的脑门)

程序员甲:自从用了 TDD,测试驱动开发之后,每天下班早了,BUG 变少了,基友不吵了。

程序员乙丙丁:真的吗?有这么神奇吗?!(集体星星眼)

程序员甲:没错,让我来给你们安利吧!

(雪花屏)Beep——

在这里插入图片描述
Hi,我是 Bruski。开头的段子纯属瞎编,但其中描述的场景:代码不按预期执行、协作的接口不可靠等等,在我们日常工作中其实挺常见的。
原因可能千奇百怪,比如在犯困的午后工作,比如没想清楚就动手等等,而且在过程很糟糕的情况下,输出还没有自动化测试去保证,那线上在跑的程序很可能就是一颗不定时炸弹。

那有没有什么办法能最大程度避免以上情况呢?我会说,不妨试试极限编程(XP)中的优秀实践:测试驱动开发吧!
在这里插入图片描述

别问,先感受

那么到底什么是测试驱动开发呢?

别急,先来感受一道小题目,非常简单:FizzBuzz。

题目模板地址: git clone https://github.com/bruceeewong/tdd-kata.git -b kick-start

题目来源:极客学院-测试驱动开发实战营


FizzBuzz 是一个简单的猜数字游戏。
在这里插入图片描述
想象你是个小学 5 年级的学生,现在还有 5 分钟就要下课,数学老师带全班同学玩一个小游戏。他会用手指挨个指向每个学生,被指着的学生就要依次报数:

第一个被指着的学生说“1”,第二个被指着的学生说“2”,如果一个学生被指着的时候,应该报的数是 3 的倍数,那么他就不能说这个数,而是要说“Fizz”。5 的倍数也不能被说出来,而是要说“Buzz”。

于是游戏开始了,老师的手指向一个个同学,他们开心地喊

着:“1!”,“2!”,“Fizz!”,“4!”,“Buzz!”……

终于,老师指向了你,时间仿佛静止,你的嘴发干,你的掌心在出汗,你仔细计算,然后终于喊出“Fizz!”。运气不错,你躲过了一劫,游戏继续进行。

为了避免在自己这儿失败,我们想了一个作弊的法子:最好能提前把整个列表打印出来,这样就知道到我这儿的时候该说什么了。

题目要求

写一个程序,打印出从 1 到 100 的数字,将其中 3 的倍数替换成“Fizz”,5 的倍数替换成“Buzz”。既能被 3 整除、又能被 5 整除的数则替换成“FizzBuzz”。

要求:

  • 代码整洁,没有重复代码
  • 有单元测试,单元测试覆盖率 100%
  • 5 分钟内完成

题目解析

相信大家应该都能很快地实现题目的要求,不过,关于单元测试部分,大家写的是否轻松呢?接下来我想给大家展示下我的做题思路——用 TDD 的方式。

测试驱动开发的要义是:测试先行,没有失败的测试,就不允许实现。所以,在动手前我们需要想清楚题目要实现什么,即拆解需求。再回顾下题目要求:

打印出从 1 到 100 的数字,将其中 3 的倍数替换成“Fizz”,5 的倍数替换成“Buzz”。既能被 3 整除、又能被 5 整除的数则替换成“FizzBuzz”。

打印出 1 到 100 的数字?也许会有人开始构思程序:一个 for 循环,if-else 一下,再 console.log 一下。等等,输出打印到控制台的话,我们怎么写测试验证输出是否正确呢?所以不妨转换下思路,沿着函数的本质:input -> process -> output来思考,其实我们要做的是:

实现一个函数

输入: 1~100 的数字

处理:

  • 3 的倍数替换成”Fizz”

  • 5 的倍数替换成“Buzz”

  • 3 和 5 的公倍数(或者 15 的倍数)替换成“FizzBuzz”

  • 其他数字则转换为字符串

输出:字符串

将需求完全拆解后,对应的测试用例也就信手捻来了,就让我们从最最简单的测试开始,函数就叫 fizzbuzz 吧,接收参数 1,返回字符串“1”。(这种直白的语法就叫断言(Assertion),即把预期输出与实际输出作对比以验证程序是否正确运行)

1
2
3
4
5
6
7
8
// 以下语法为Jest.js的测试写法
const fizzbuzz = require("./fizzbuzz");

describe("fizzbuzz", () => {
test("测正常数字返回", () => {
expect(fizzbuzz(1)).toEqual("1");
});
});

执行jest命令运行测试,结果不出所料地报红了:fizzbuzz is not a function,毕竟我们此时连函数都没声明。

在这里插入图片描述

那我们赶紧定义函数:

1
2
3
4
function fizzbuzz(num) {
return "1";
}
module.exports = fizzbuzz;

有人会说,函数体返回常量,你在骗自己吗?别急,再执行一下 jest 命令运行测试:
在这里插入图片描述
Yes,测试通过,变为绿色!没错我是硬编码返回了,但这是 TDD 的第二个重要的要义:只写让测试恰好通过的代码。好吧我知道留着这样的代码,是不敢入睡的,那就再多加一条测试:

1
2
3
4
5
6
7
8
const fizzbuzz = require("./fizzbuzz");

describe("fizzbuzz", () => {
test("测正常数字返回", () => {
expect(fizzbuzz(1)).toEqual("1");
expect(fizzbuzz(98)).toEqual("98");
});
});

再运行测试,闭着眼睛都知道会失败,自动化的测试还非常贴心地帮我们指出了期待输出和实际输出的差异。
在这里插入图片描述
有了失败的测试,我们才开始动手写实现,实现也相当简单:

1
2
3
function fizzbuzz(num) {
return num.toString();
}

执行测试,OK,测试通过,结果又变回绿色。
在这里插入图片描述
这时候我们知道第一条需求已经被解决,无情划掉它:

  • 3 的倍数替换成”Fizz”
  • 5 的倍数替换成“Buzz”
  • 3 和 5 的公倍数(或者 15 的倍数)替换成“FizzBuzz”
  • 其他数字则转换为字符串

那就写下第二条测试用例:

1
2
3
test("测3的倍数返回", () => {
expect(fizzbuzz(3)).toEqual("Fizz");
});

执行测试,结果 3 原样返回,测试不通过:
在这里插入图片描述
又是一条失败的测试,快速实现它让它翻绿!

1
2
3
4
5
6
function fizzbuzz(num) {
if (num % 3 === 0) {
return "Fizz";
}
return num.toString();
}

再次运行测试:
在这里插入图片描述
那此时再加几条测试,结果肯定是正确的:

1
2
3
4
5
test("测3的倍数返回", () => {
expect(fizzbuzz(3)).toEqual("Fizz");
expect(fizzbuzz(6)).toEqual("Fizz");
expect(fizzbuzz(99)).toEqual("Fizz");
});

再划掉一项需求!

  • 3 的倍数替换成”Fizz”
  • 5 的倍数替换成“Buzz”
  • 3 和 5 的公倍数(或者 15 的倍数)替换成“FizzBuzz”
  • 其他数字则转换为字符串

接下来想必大家都知道了,复制一下 3 的测试用例,改成 5,然后执行测试,失败,然后复制一条 if 语句….等等!难道你忘了,Copy-Paste 是魔鬼吗?难道我是在教你成为一名 CV 工程师吗?好了,这里引出 TDD 又一条要义:消除所有重复。其实通过将运算抽象为函数,很容易就能消除重复的直白代码:

1
2
3
4
5
6
7
8
9
10
11
12
function fizzbuzz(num) {
function canDivideBy(num, divideNum) {
return num % divideNum === 0;
}
if (canDivideBy(num, 3)) {
return "Fizz";
}
if (canDivideBy(num, 5)) {
return "Buzz";
}
return num.toString();
}

运行测试,干净漂亮地通过了测试:
在这里插入图片描述
最后再补充一条 3 和 5 的公倍数测试用例,使用抽象好的函数实现,运行测试,测试通过后,那么整个需求就完成了。下面是完整的测试用例&实现&测试截图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// fizzbuzz.test.js
const fizzbuzz = require("./fizzbuzz");

describe("fizzbuzz", () => {
test("测正常数字返回", () => {
expect(fizzbuzz(1)).toEqual("1");
expect(fizzbuzz(2)).toEqual("2");
expect(fizzbuzz(98)).toEqual("98");
});
test("测3的倍数返回", () => {
expect(fizzbuzz(3)).toEqual("Fizz");
expect(fizzbuzz(6)).toEqual("Fizz");
expect(fizzbuzz(99)).toEqual("Fizz");
});
test("测5的倍数返回", () => {
expect(fizzbuzz(5)).toEqual("Buzz");
expect(fizzbuzz(10)).toEqual("Buzz");
});
test("测3和5的公倍数返回", () => {
expect(fizzbuzz(15)).toEqual("FizzBuzz");
expect(fizzbuzz(45)).toEqual("FizzBuzz");
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// fizzbuzz.js 实现
function fizzbuzz(num) {
function canDivideBy(num, divideNum) {
return num % divideNum === 0;
}
if (canDivideBy(num, 15)) {
return "FizzBuzz";
}
if (canDivideBy(num, 3)) {
return "Fizz";
}
if (canDivideBy(num, 5)) {
return "Buzz";
}
return num.toString();
}

module.exports = fizzbuzz;

在这里插入图片描述
划掉所有需求,爽,可以下班了!

  • 3 的倍数替换成”Fizz”
  • 5 的倍数替换成“Buzz”
  • 3 和 5 的公倍数(或者 15 的倍数)替换成“FizzBuzz”
  • 其他数字则转换为字符串

最后,执行 Jest 命令jest --coverage生成测试覆盖率报告:
在这里插入图片描述
怎么样?100%的测试覆盖率,没有重复、多余的代码,漂亮地完成所有需求。如果你不放心,多加几条测试用例,多运行几遍测试命令,这就是测试驱动开发产出的有质量保证的代码。

有了自动化测试做保障,测试通过,我就敢说在我所预见的情况中,他会一直通过,除非,除非产品经理的需求又变了…
在这里插入图片描述
总结一下,在做 FizzBuzz 题目的过程中,用 TDD 的节奏开发流程如下图:执行测试覆盖率检查

如果大家有关注到图中的颜色,那么请大家跟我念一句 TDD 的咒语,念三遍:

Red / Green / Refactor

Red / Green / Refactor

Red / Green / Refactor
在这里插入图片描述
这就是贯穿测试驱动开发的整个流程的循环,也是 TDD 的节奏。其中:

  • Red表示测试不通过时,IDE 的状态条报红,此时我们有了失败的测试;
  • Green表示测试通过时,IDE 的状态条回到绿色状态,此时通过快速小步地开发,让测试恰好通过;
  • Refactor表示重构代码,消除所有重复的逻辑。

接下来,让我们跟随 Kent 大叔深入地琢磨下测试驱动开发吧!

深入测试驱动开发

到底什么是测试驱动开发(Test-driven Development)呢?

按照 Kent 大叔的原话:

TDD is an awareness of the gap between decision and feedback during programming, and techniques to control that gap.

粗浅地翻译过来意思是:

TDD 是一种编程意识,关注着程序设计与实现结果反馈之间的间隙;同时 TDD 也是用以填平该间隙的一系列技巧。

是的,概念总是过于晦涩与抽象。作者还提供了不同角度的定义来帮助理解:

  • 测试驱动开发是一种管理编程中的恐惧的方式。(Test-driven development is a way of managing fear during programming. )
  • 测试驱动开发是一种开发风格:我们通过自动化的测试来驱动开发(we drive development with automated tests, a style of development called Test-Driven Development)

你一定要记住,TDD 的终极目标是:

Clean code that works. (产出干净且可用的代码)

这是《测试驱动开发》序章的第一句话,也是我编程的座右铭。

TDD 开发模式

首先我们要搞清楚 3 个问题:

  1. 什么是测试?
  2. 测什么?
  3. 什么时候测试?

什么是测试

测试作为动词,是“去验证”的意思。测试作为名词,是对预期得出可接受或者不可接受判断的一个过程。

按 Kent 大叔的意思是:

测试是开发者的基石,也是将对程序运行结果从未知的恐惧转化为熟知的手段。

不喜欢写测试的程序员,通常将经历这样的消极循环:感到恐惧,因为没测试 -> 越恐惧,压力越大 -> 压力越大,越不会测试。

而善用测试的程序员呢?正向循环应该像这样:越感到恐惧,越执行测试 -> 越测试,恐惧越小 -> 压力越小,越愿意测试。

测什么

我们这里指的,是程序级别的单元测试(Program Level),主要关注逻辑与数据。

对于逻辑的测试,一般来说等同于需求,我们要对需求进行编程级的拆解,即要能拆解为可以动手编码的若干步骤,通过不断地写下你的期望与实际输出的测试语句(即断言),然后实现代码让其通过,从而一步步达成目的。

对于数据的测试,这里我也没有很多实践,有几点可以分享:

  1. 不要使用真实的数据(数据库数据、网络请求等)
  2. 按照预期的数据结构,构造直观的伪造数据来满足测试。

什么时候测试

按照测试驱动开发的节奏,每当:

  • 动手编程前,先写出一条会失败的测试
  • 重构前,保证测试通过

了解完前置概念后,又该怎么落笔我们的第一个测试用例?

Red Bar Patterns

Red Bar,顾名思义:因执行测试失败而显示红色的状态栏

要让测试失败,那首先要写下你的测试,我们上一节介绍了需求拆分,得到了一份 todo-list,那我们究竟该自顶向下实现(即从大问题的具体用例开始实现),还是自底向上实现(从小模块开始,再逐步聚合)呢?

其实这两者都不能很好描述 TDD 的实现过程,准确地说,实现顺序没有太大关系,因为 TDD 是基于 known-to-unknown 的模式进行,即用已知的来推出未知的。我们在拆分需求为一条条可编程验证的用例时,就是将未知的庞然大物拆解成不废力气就能达成的小目标,我们知道如果一步步实现了所有子测试,最终需求就能实现。

在 TDD 这里,万事开头难,但测试开头易。第一个测试应该写一条测什么都不做的操作的测试,这里看似没什么意义,但是它确实验证了:

  • 这个操作属于哪里?
  • 什么是正确的输入?
  • 什么是基于正确输入的正确输出?

Green Bar Patterns

Green Bar,顾名思义:因执行测试成功而显示绿色的状态栏

在 FizzBuzz 实现的过程中,我们用到了几种快速让测试通过的技巧,分别是:

Fake It (‘Til You Make It) 伪造数据

比如在 FizzBuzz 最开始的时候,为了让测试通过,直接在函数里返回常量。

为什么要写早晚要换掉的实现?原因有两点:

  • 心理暗示
    • 测试成功比测试失败好
  • 范围控制
    • 专注在解决当前测试上,避免过度设计
    • 保证当前代码始终可用

Triangulate 三角测量

  • 从不同角度测试代码,让伪造数据的代码失败,然后抽象、实现,让测试通过。例如我们前面用两条测试,宣告了硬编码返回”1”的代码实现的死亡。

Obvious Implementation 最简实现

  • 既然用例已经拆分成小步,一定可以快速实现,否则,反思步子是否迈大。

  • 写恰好实现的代码。

至此,结合 FizzBuzz 的解析,我们已经体验完测试驱动开发最核心的流程。

来总结下吧

再回到定义,测试驱动开发本质上是一种编程思考和实践的一种风格/方式,比起一开始的顶层设计,他更关注需求与实现之间的距离,要求程序员能拆解成若干可测试、可实现的步骤,然后借助自动化测试工具,按照一定的节奏Red / Green / Refactor、测试技巧和编程手法,从而产出干净的、可工作的代码。

  • 因为测试先行,倒逼我们必须思考清楚问题应该如何解决,避免了低效地走一步看一步的浑浑噩噩;
  • 因为测试先行,我知道做到什么程度算完成,并且自信地认为在我所预期的情况内,程序可以良好地工作。
  • 测试用例可以作为更棒的注释而存在,让协作的同事更清楚地知道函数的用途和用法。
  • 提交代码时,看着绿色的状态栏,心情愉悦,安心下班!

TDD 的挑战

TDD 更多的应用在程序级别的单元测试,这一块是开发人员完全自主掌控的部分。而在此之外的一些场景,TDD 也许就不那么合适,比如:

  • 对于 GUI 的测试(网页、App 级别的 UI 测试)
  • 对于依赖数据库的测试(通常我们使用 mock 对象测试)
  • 不要去测第三方的代码,那应该有他们的开发去保证(如框架等)
  • 不能测试编译器之类的东西。

写在最后

作为一名 Web 前端开发,在开发业务逻辑时,我都会有意识地使用 TDD 的方式来实现。(UI 方面的测试实践并不多,还要继续学习!)
TDD 测试驱动开发带给我的开发体验是:

  1. 享受可预测、尽在掌握的开发体验
    1. 当通过了所有测试、开发也就结束了
    2. 并且开发结束了,可预见的场景不会有太多 bug
  2. 给自己留一瓶后悔药
    1. 第一次的实现可以很烂,但只要有测试,再回过头来,只要测试是通过的,就可以放心地重构。
    2. 如果祖传代码没有测试,那就尝试找到程序输入输出的接缝处,给他补充测试,这样可以最大程度确保重构不会大刀阔斧地破坏原有逻辑。
  3. 保持代码年轻的秘诀?
    1. 如果是测试驱动出来的代码,将拥有输入输出清晰、幂等性特点。
    2. 每当添加新特性前,先思考清楚,先写测试,代码不会随乱涂乱改而腐败。
  4. 同事协作时之间更放心
    1. 你产出的代码值得信赖。
    2. 同事也用 TDD,看着测试用例就知道怎么用了,真香。

这篇文章只是展示 TDD 的基础玩法,想要深入了解测试驱动开发,去读下 Kent Beck 的 《Test-Driven Development By Example》,感受 Kent 大叔的幽默与智慧吧。
在这里插入图片描述
愿你的程序无 Bug,早点下班!
在这里插入图片描述

CSS 层叠样式表作为前端三剑客之一,通过各类选择器来解耦 HTML 结构与表现,让开发者拥有专注控制样式的能力,实现了关注点分离。通过层叠机制,为规则赋予不同的重要程度,让我们的样式代码能够灵活地继承与覆盖。

它就像精灵宝可梦里的百变怪 👾,拥有强大而奇妙的变化与适应能力,前端技术也因 CSS 的加入而变得漂亮与有趣 🤩

本文主要分享 CSS 技术中的一个切面概念:渐进增强,希望能够从原理+实例出发,给大家在设计、编写网页样式时带来一点启发 💡

渐进增强

随着 HTML 与 CSS 的发展,许多新特性陆续得到浏览器厂商们的支持,比如 HTML5 中的新增标签,CSS 中的 Flexbox,Grid、calc 等,给网页带来了很多新鲜而强大的能力。
但是由于时代推移、浏览器的厂商各自对规范实现的不一致,导致不同的浏览器以及版本存在新特性不兼容或者其他 Bug 问题,让开发者在新特性与兼容性之间放弃了尝试新特性的想法 🤪

但是!时代与技术一定是在进步的,而在新老交替的过渡期,我们可以采用渐进增强的策略,来平衡向后兼容性与最新的 HTML/CSS 特性。

什么是渐进增强 🧐

⛳️ 所谓渐进增强,即我们首先为最小公分母准备可用的内容,然后再为支持新特性的浏览器添加更多交互优化。

要实现渐进增强,意味着代码要分层,每一层增强代码都只会在相应特性被支持或被认为适当的情况下使用。听起来有点复杂,然而实际上 HTML 与 CSS 已经部分内置了这一策略。

HTML 的渐进增强策略

⛳️ 对于 HTML 而言,浏览器遇到未知元素或属性时并不会报错,也不会对页面产生什么影响。

假设表单中有输入邮件的控件:

1
<input type="text" name="field-email" />

我们就可以使用 HTML5 新增的 email 类型,可以拥有邮件格式检验以及移动设备中的特定格式软键盘的强化能力:

1
<input type="email" name="field-email" />

尚未实现这个新类型的浏览器就会想:”这是甚么玩意儿?不明白“🧐,然后给他退化成 type=”text”类型,就当无事发生。
这样,我们既渐进增强了页面,也不会对浏览器产生什么不好的影响。
类似的还有 HTML5 的文档声明,这样的语法也是向后兼容的。

CSS 的渐进增强策略

⛳️ 对于 CSS 来说,无法识别的属性/值都会被浏览器丢弃,所以只要提供合理的后备声明,使用新属性就不会带来不良后果。

例如,现代浏览器支持的颜色值 rgba 函数,我们可以这样定义红色:

1
2
3
4
.overlay {
background: #000;
background-color: rgba(0, 0, 0, 0.8);
}

对于旧的浏览器,它会丢 rgba 的声明,应用第一条规则;对于现代浏览器,第二条规则就会覆盖第一条,显示带透明度的红色。
那么即使不是所有浏览器都支持 rgba 函数,由于提供了合理后备,我们仍然可以大胆地使用它 😎

如何实现渐进增强 🧐

1. 厂商前缀

🛠 浏览器厂商也基于同样的原理为自家浏览器引入实验特性,并加上一串特殊前缀,这样其自家浏览器就能识别而其他浏览器忽略。
例如 transform 属性:

1
2
3
4
5
6
.thing {
-webkit-transform: translate(0, 10px);
-moz-transform: translate(0, 10px);
-ms-transform: translate(0, 10px);
transform: translate(0, 10px);
}

下面列举前缀对应的浏览器厂商:

-webkit-: 适用于 Webkit 内核的浏览器,包括 Blink 引擎的(基于 Webkit)

  • Safari
  • Chrome
  • Opera

-moz-: 基于 Mozilla 浏览器

  • Firefox

-ms-: 微软家

  • Internet Explorer

2. 条件规则与检测脚本

@supports块
⚙️ 如果希望通过检测浏览器是否支持某个 CSS 特性来提供样式,可通过条件规则检测:

1
2
3
@supports (display: grid) {
/* 编写网格布局的规则 */
}

3. JavaScript 库

⚙️ 通过 Modernizr 这个 JS 库可以检测各种特性的支持情况。
原理是 modernizr 会根据检测情况给 html 根元素添加相应特性的类名,如 flexbox,然后在编写 CSS 时带上前缀,即可安全的增强样式。

更多关于新特性的浏览器支持情况,可以到 http://caniuse.com 查阅 🔎

🌰 例子 1:在浮动之上应用 Flex

这里有一个使用 float 布局实现的卡片布局,我们来看看如何在不破坏原有代码的情况下,用 Flexbox 特性增强这个页面。

这个页面的使用了 float 来实现网格布局效果,主要原理是

  • 💡 使用 col 类名定义浮动组件,row 类名定义清除浮动的父组件
  • 💡 加上 row-quartet 定义子项列宽,实现网格布局效果
  • 核心代码如下:
1
2
3
4
5
6
<div class="row row-quartet">
<div class="col"><article></article></div>
<div class="col"><article></article></div>
<div class="col"><article></article></div>
<div class="col"><article></article></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
// index.css/* 行组件 */
.row:after {
content: "";
display: block;
clear: both;
height: 0;
}
.row-quartet > * {
width: 25%;
} /* 列组件 */
.col {
float: left;
}

通过图片可以看到上述代码并未实现列等高效果,然而要实现浮动元素的列等高效果比较麻烦,详情方案参考:https://juejin.cn/post/6844904048278290440#heading-22

而使用 Flexbox 可以轻松实现,原理是 Flex 的等高机制:父容器定义为 Flexbox 容器之后,控制辅轴的属性 align-items 默认为 strech,即自动拉伸子项的高度以填满空间。

那么此时我们想通过 Flexbox 来实现列等高效果,又不破坏已实现的部分,该怎么做呢 🧐
⛳️ 答案是:引入检测机制,在检测块内编写新特性的规则。

上面也提到有两种方式:

  1. 使用 @supports 块检测,但由于此检测语法本身就比较新,所以对于旧浏览器不友好。
1
2
3
@supports (display: flex) {
/* 编写 flexbox 布局的规则 */
}
  1. 使用 Modernizr 库检测,然后使用带.flexbox 前缀的类名来编写增强的规则。

首先我们将 Modernizr 的 script 写在所有 link 的前面(保证检测脚本先加载)。

添加后,Modernizr 检测完毕后,我们的标签上就将支持的特性名字生成 classname,测试浏览器是当前最新的 Chrome 89, 所以可以看到 flexbox 赫然在列。

接下来就基于此类名前缀,编写增强代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// index.css​/_ flexbox 增强代码 _/
.flexbox .row {
display: flex;
}
.flexbox .col {
display: flex;
flex-direction: column;
}
.flexbox .col > _ {
flex: 1;
}
​/_ float 实现相关代码 _/.row:after {
content: "";
display: block;
clear: both;
height: 0;
}
.row-quartet > _ {
width: 25%;
}
.col {
float: left;
}

我们来看下效果,这里用到浏览器测试工具:browserstack ,选择在 Chrome 下打开页面,展示了等高效果,说明应用增强代码:

而在 IE 10 浏览器上就降级为无 flexbox 的效果:

查阅 CanIUse 网站,看到 IE10 只支持带 -ms- 前缀的 flexbox 属性,所以也解释了上述表现。

总结一下 在浮动之上应用 Flex 方案,有几个关键点:

  • 不理解 flex 关键字的浏览器会忽略它。
  • 给标签定义了 display: flex 之后,浏览器会忽略该标签定义的 float,clear 与 display:inline-block 等声明。

举一反三,其他场景我们也可以遵循首先写一个适合任何场景的布局,然后再通过 新特性检测 + 运用 Flexbox / Grid 等新特性来渐进增强我们的网页。

详情代码:CodePen:

🌰 例子 2:响应式图片

响应式 Web 设计出现以后,何时加载合适的图片是前端开发者要面对的问题。很多开发者不管设备、屏幕大小,一律使用相同的图片,这种做法导致小屏幕看大图浪费带宽与内存、大屏幕展示小图看不清楚等问题。而浏览器会对网页进行预处理,图片等资源会在浏览器构建完页面或运行 JavaScript 之前就开始下载,这意味着不可能仅凭脚本就完美解决图片响应式的问题。所以 HTML5 提出了响应式图片的解决方案。

响应式图片(Responsive Image)是指给 HTML5 规范给标签添加的 srcset、sizes 新属性,旨在解决不同的条件下告诉浏览器加载不同的图片。

  • srcset: 哪个是当前图片的可替换源文件,其宽度是多少像素?
  • sizes: 在各个断点中,图片的 CSS 宽度是多少?

Hi,img 标签的新属性

来看个例子 🌰:

1
2
3
4
5
6
7
8
9
<img
src="http://dummyimage.com/300x150"
srcset="
http://dummyimage.com/300x150 300w,
http://dummyimage.com/600x300 600w,
http://dummyimage.com/1200x600 1200w
"
sizes="(max-width: 600px) 300px, (max-width: 1200px) 600px, 1200px"
/>

分析一下:

  • srcset 的值是一组图片 URL+实际像素宽度(不是 CSS 像素),定义了一组资源后,还要告诉浏览器怎么使用这些图片。
  • sizes 通过开头可选的媒体查询条件 + 图片展示宽度值(CSS 单位,可为 px, em, vw)。

如果某条媒体查询条件为真,浏览器将取得条件后面定义的宽度,然后去匹配到最接近尺寸的图片,执行下载。最后,图片将按照定义的宽度进行展示。

在 Chrome 上运行的效果:



需要注意的点:

  • 在有图片缓存的情况下,浏览器可能会加载较大的图片。
  • 在网速慢或者电量低的情况下,浏览器会加载较小的图片。
  • 浏览器知道当前设备是不是高分辨率屏幕,从而决定是否自动加载大图。

通过 srcset,我们渐进增强了原有的图片组件,让不同宽度的屏幕加载更合适尺寸的图片。

进一步加强:picture 标签

MDN 介绍:picture 元素除了在多个不同分辨率的图片间切换,还有几个很重要的响应式图片的应用场景:

响应式图片在大小屏幕分别需要不同的裁切方式,根据浏览器的支持加载不同格式的图片。如 Google 的 Webp 格式会比 JPG 体积小 25%-34%,可以优化网页的性能。

这些问题的标准解决方案是:picture 元素,他作为 img 元素的容器,同时扩展了 srcset 和 sizes 的能力。举个例子:

1
2
3
4
5
6
<picture>
<source
type="image/webp"
srcset="https://www.gstatic.com/webp/gallery/4.sm.webp" />
<img src="https://www.gstatic.com/webp/gallery/4.sm.jpg" alt="tree"
/></picture>

picture 元素包含 source 标签与 img 标签,在支持 picture 与 webp 格式的浏览器下,就会加载 webp 格式的图片;否则忽略将 picture 与 source 元素,应用 img 标签中的 jpg 格式图片。以下是在 browerstack 的测试情况:图一在 IE11 上测试,IE11 不支持 webp 图片,所以加载 jpg。

Chrome85 支持 webp,加载 webp 图片

除此之外,picture 还可以添加 media 属性,结合媒体查询进一步控制浏览器选择图片的逻辑:

1
2
3
4
5
<picture>
<source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg" />
<source media="(min-width: 800px)" srcset="elva-800w.jpg" />
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva"
/></picture>

对于开发者来说,我们在不破坏 img 原有能力的基础上,大大增加了操控图片的可能性,也得感谢 HTML 的渐进增强策略。

总结一下

在前端新标准、新技术百花齐放的时代,我们应该积极关注、拥抱新技术,用渐进、可降级的方式去为旧项目赋能焕新 ✅ 其实渐进增强,在我看来是一种严谨、巧妙的思维方式,感受其中的分层、容错、可扩展等设计理念,并尝试运用在日后的编程中吧 🤓

辅助工具

这里就列举几个辅助工具,有助于我们写出有良好兼容性的样式代码:

  • PostCSS 预处理器插件 - Autoprefixer, 根据 CanIUse 网站与你项目所支持的浏览器,自动为你的 CSS 代码添加相应前缀。
  • 静态分析及 Linter- Stylelint,能检查语法错误,也能检查选择符/声明中的有问题的规则。
  • 浏览器测试工具:Browserstack,是一款基于 Web 的实时浏览器测试工具,支持虚拟机测试全平台&浏览器版本,收费 💰
  • HTML/CSS/JS 兼容性查询平台:CanIUse

相关资源

  • 《精通 CSS 高级 Web 标准解决方案》(第三版)——埃米尔·比约克隆德
  • MDN - Responsive images