2022年12月

前言

这是一篇非常草率的2022总结文章, 我会更多的介绍关于技术方向, 以便同行们在明年有更多方向和想法. 如何形容2022年呢? 总的来说就是“进步的太慢, 或者说根本没有实质性的进步”; 导致这个问题的原因有很多, 简单的说就是被一些垃圾工作缠绕住了.

工作

2022年我仍然在西安, 2023年将是我工作的第四年, 作为一个软件从业者, 我可能在西安混的还算看得下去, 但是只有我自己知道, 我其实什么也不是. 因为西安这个城市造就了互联网行业注定没有氛围, 周围的同行技术不错的太少了. 我以前想这可能和我的圈子或者西安(地域)有关系. 事实上我们只要下班之后多学一点多看一会, 就可以超越大部分人了. 学习对于程序员来说是最简单的事情, 为什么大部分程序员都没有这样的习惯, 是因为大家都被结婚, 孩子, 父母, 傻逼领导等各种事情困住了, 我想等我年龄大一点可能也会变得不爱学习了.

我今年接触了很多一线城市的开发, 发现整个中国软件行业都太浮躁了, 换句话说优秀的团队太少了. 企业喜欢赚快钱, 大多数企业不会关心技术, 他们注重结果混KPI, 有时候甚至不需要展示demo演示程序, 一个ppt足以征服领导. 这种公司通常有以下几个特征:

  1. 设置了日报和周报
  2. 喜欢开会
  3. 没有加班费
  4. 每1,2周发一次版本, 美其名曰敏捷开发
  5. 每个公司都会有一个卷王, 半夜三更提交代码, 也不知道公司给他了多少钱
  6. 测试, 前端, 后端互相甩锅
  7. 裙带关系
  8. 地域歧视

如果你的公司碰巧都有以上症状, 请你立即下班开始学习, 你要努力提升自己, 摆脱这种工作环境. 我今年划水机会蛮多的, 在朋友圈和各个地方吐槽了很多岗位, 比如产品经理和前端, 这两个岗位是最浮躁的, 我甚至觉得这两个岗位让2个高中生过来都能完成, 如果我是老板, 我就会雇佣高中生来替代他们 (事实上我也是前端)

产品经理的jd我会写:

  1. 熟练使用微信 (例如转发消息)
  2. 熟练使用腾讯会议
  3. 熟悉互联网黑话 (能抽时间做一下么 | 我们能不能临时出一个给老板看的版本)

前端的jd我会写:

好吧, 我承认前端的门槛比产品高多了, 至少产品可以不用出高保真原型和文档, 只需要转发消息就可以把产品搞定, 我觉得是一个中国人都会;

我今年在工作中收获很多, 得到了很多人的帮助, 他们带我成长, 比如说在上一家公司的康凯哥, joe, 枨, 聪, 测试双煞和其他技术部同僚, 他们真诚, 负责, 为我解决和分担了不少麻烦; 其中凯哥和joe在技术上帮助我了很多, 到目前凯哥在我心目中仍然是榜样的存在, 因为他才华横溢而且不厌其烦的为我解答问题; 同样的还有joe, 和他一起共事, 是一段非常难忘的经历. 在新公司中, 我和我的同窗好友(基友)在一起工作的体验是我从业以来最爽的, 因为我们在一起会有当时一起学程序的美妙感觉, 以至于我和他在一起配合, 比去大厂还要更好. 所以我跳槽时, 我没有准备任何面试, 毅然决然和他一起战斗.

技术

在技术这一篇中, 我会聊的更多一些, 我们先从公司项目说起吧, 我今年接触了万恶的flutter来构建一个史一样的app, 尽管我们没有任何架构可言, 写出了和我便秘20天拉出的史一样的又臭又长的代码, 但是打包出来, 我们惊讶的发现, app居然如此流畅, 这让我认识到了四件事:

  1. flutter真吊
  2. 对js又爱又恨
  3. reactnative要完蛋
  4. 原生开发要被抢占一大部分份额

但是flutter的开发体验太糟糕了, dart的标准库设计对标es6, 简直是天大的差距; 而且最头疼的生态问题仍然很难解决, 比如地图, 图表仍然没有很好的支持. 而且对于前端而言上手难度颇大, 因为html和flutter ui抽象机制完全是思维的转变, 而且flutter/dart的编译器简直是太糟糕了, debug难度非常高, 如果离开了google和ai, 我很怀疑我能不能用flutter开发第二个app.

希望dart标准库能够多多迭代, 参照es6的设计好好改进, flutter是一个优秀的跨平台app开发框架, dart的唯一用武之地就是服务这个框架. 这相对于js不同, js的核心标准不变, 但是需要对不同设备和环境扩展api, 比如浏览器前端开发app时, 他会学习一些新的东西来挑战和质疑已经存在的知识, 这一定程度上会造成知识混乱. 但是开发app不同, 运行环境相对单一, 目的明确, 我们只需要知道开发网页需要使用js, 开发app需要使用flutter, 这就够了, 这就是最优解. 希望2023年flutter能够干掉reactnative一统跨平台app天下.

如果有人问我跨平台app推荐什么技术, 我会毫不犹豫的说flutter, 尽管它有太多缺点.

《Flutter实战·第二版》
猫哥flutter课程


我们再来说说前端框架吧, 今年我是react黑粉, 同样的也是vue3黑粉, 我使用vue3已经快2年多了, 2020年我就写了vue3教程, 甚至在去年我写了一部分vue3的电子书. 但是后来放弃了, 因为我对浏览器的东西渐渐失去了兴趣, 在上一家公司做了不少网页的东西, 让我意识到这对职业发展很不利. react难么, react-hook我学了一个小时就能写一个完整的线上应用, vue3难么, 我学了几个小时就能写教程. 不是我有多厉害, 是因为框架的团队太强了, 它们把“开发者养懒了, 它们是技术罪犯

当一个技术变得特别简单的时候, 就开始卷了, 它们试图让开发者写更少的代码能完成更多需求, 如果要找到一份心仪的工作, 你不得不去卷源码, 就和java一样, 你得在面试的时候准确说出jvm源码中的函数名称, 有时候函数名称有几十个单词组成, 你能用标准的英语读出来, 就能震慑傻逼面试官. 前端已经快要和java一样卷了, 现代前端才存在多少年, java已经存在几十年, 可见前端的发展速度令人震惊, 我们如果要想在这个圈子有更多机会, 我们需要逃避, 要去更换赛道.

每一个框架都有或多或少的缺点, react hook无处不在的编程陷阱, 我今年开发在线ide, 就被react各种陷阱逼疯了, 同样的功能使用vue3能节约60%的代码; 反观vue3, 它的ts支持也是非常糟糕的, 如果没有vite, volar等优秀的插件生态, 我的第一框架首选也不会是vue3. 在今年有很多框架值得我们去关注, next, nuxt, astro, qwik, svelte, solidjs, preact.

  1. 如果你喜欢react的api设计和虚拟dom框架, 我强烈推荐你使用preact.
  2. 如果你喜欢极简干净短小的js代码运行在你的网站上, 推荐svelte
  3. 如果你喜欢island架构, 推荐你使用astro
  4. 想玩好全栈框架, 推荐nuxt3和nextjs, nextjs有swc, nuxt3有我的代码和强大的api引擎

vite是今年前端宠儿, 也是我的心头爱, 鱿鱼丝是一个才华横溢的程序员, 他是一个天才, 有着独到的想法, 而且vite的团队技术都很顶尖, vite已经4.0了, 我有空一定要拜读一下vite的源码, vite在2023年的优化空间有限, 因为vite本质上的原理没有特别复杂, 它进一步的简化了开发模式和优化程序体验, 我们摆脱了webpack噩梦. 得益于插件系统, 理论上vite可以基于nodejs做出很多前端领域的扩展, 甚至很多nodejs框架也在集成vite.

在今年稍晚的时候, turbo团队发布了它们的构建工具turbopack, 我对于turbopack持乐观状态, 但是我不认为它能打败vite, vite的生态很庞大, 在前端界中生态就是王道, 就像npm一样, 再如何吐槽npm/nodemodules设计, 它仍然是最网红的依赖管理工具. turbopack我们应该关注它的核心技术turbo, turbo团队还有一个作品是turborepo, 也是解决monorepo的杀手锏框架, 它们很擅长利用缓存来优化一个流水线, 并且在turborepo中甚至可以将缓存共享到ci/cd中 (我记得是, 看到过相关功能), 并且它们推出了云服务, 对于企业用户而言, 在构建一些微应用/大型代码库和框架, 是非常适合使用turbo团队的作品的.

顺便再说一句, 不要学习webpack了, 我们不得不承认, webpack是一个优秀的构建系统, 理论上它能完成任意复杂度的产物构建工作, 但是它太重了, 而且大多数团队缺乏webpack技术沉淀, 而且大多数业务不需要庞大构建系统支持, 我们甚至只需要vite的默认配置进行打包, 性能可能会更好.


在今年我写了一个nodejs框架, 也就是sword.js, 我很喜欢nodejs, 我很喜欢用nodejs来构建后端应用, 简单易用, 但是swordjs也是我最后一个用nodejs写的后端开源项目, 我不得不抛弃nodejs. 是因为nodejs和我之后的发展方向存在一定冲突, 但是我仍然在前端领域频繁用到nodejs, 如果你是一个前端, 请务必学习nodejs, 它太简单了, 使用nodejs你可以做任何很酷的事情. 但是请允许我介绍一下deno, 和我为什么要使用deno?

deno是一个js运行时, 它原生支持js和ts和wasm, 请记住wasm这个技术. deno是使用rust编写的, 它也是构建在v8之上的. 我之后的发展方向是区块链, 大量的要和rust打交道, 那么deno和rust中的联系就由wasm构建, 我可以使用我熟悉的语言(typescript/rescript)开发api, 并且我可以直接通过deno运行rust的合约代码, 因为rust/ink编译出的合约代码就是wasm. 而wasm是一种类汇编语言, 是一个低级语言, 它是作为c++, rust等低级语言的编译目标而出现的.

wasm的性能非常优越, 它已经在前端音视频领域取得了巨大成功, 也在浏览器中作为js的补充出现, 会比同等功能的js代码更加高效, 更接近本地速度运行, 所以我认为wasm是未来, 也是区块链的未来. 你可能会说nodejs也可以写区块链应用呀? 使用deno仅仅是因为它能直接运行wasm吗?

答案不是, 如果是半年前, 我不会使用deno, 因为我说过生态是最重要的, 但是deno在下半年兼容了nodejs绝大部分api, nodejs作者也是deno的作者, 他曾经说不会兼容nodejs的api, 但是现在为了生态做了妥协. 此时此刻, deno不仅可以兼容nodejs代码, 还可以直接运行ts和wasm, 还具有无与伦比的安全性和速度. 我铁定站它!


我们继续聊一下typescript, 我曾经是ts的狂热粉丝, 但是今年我把类型挑战的中等题刷完之后, 我改变看法了, 我不需要类型体操, 它在业务中使用的地方很少, 尽管它为重构和构建应用带来了很大的好处, 但是我们不得不承认, 要深入ts不是那么容易的, 而且在工作中我们只需要学会低阶的ts语法就可以给我们带来很大的开发益处: “给开发者良好的类型提示, 消除隐藏错误”

但是我们有没有想过, 就算写出花一样的ts代码, 对应用性能和速度有任何益处吗? 答案是没有的, 编译出来的js仍然可能存在性能和类型错误, 因为超级多的人喜欢any, 和关闭类型错误提示; 我们需要一个语言在特殊时候代替typescript, 我认为是rescript, 它可以编译性能更好的js代码, 具体的好处我就不介绍了. deno + rescript + rust/ink + flutter将是我进入区块链的理想前后端技术栈


关于开源, 我今年大部分时间都在写我的swordjs框架, 目前可以跨nodejs server和unicloud nodejs端, 有着优秀的ts运行时校验等等功能, 结合vscode插件和编译器, 无论在开发还是生产环境, 都有畅快的开发体验, 实际上它拉高了nodejs后端api应用开发速度.

结束

就写这么多, 希望大家2023年顺利, 下面是我2023年的发展方向, 希望给大家一些启发:

  1. 进入区块链行业 (重要)
  2. 重新审视所有技术栈, 对技术栈进行换血 (重要)
  3. 学会rust和deno以及rescript相关技术栈 (重要)
  4. 学英语
  5. 对程序语言理论和编程基本功进行针对训练, 比如看一些相关书籍, 编译器知识, 程序语言, 算法等 (重要)
  6. 有空学习街舞
  7. 渐渐放弃前端, 尤其是常见的业务, 比如浏览器, 小程序方向
  8. 加强后端语言学习, 比如go, 我对go比较青睐, 它写api真的挺不错的
  9. 看情况适当了解docker, k8s
  10. 对设计和产品领域持续输出新的idea, 适当做一些练习和创业项目

课程介绍与编译技术概论

这个课程使用rescript进行学习, 主要的学习目标就是实现一个编程语言. 为什么要使用rescript去学习, 主要是因为rescript是元语言, 也是ocaml的一种方言; 这个语言并不是一种js类型加强 (例如typescript), 而是选取了一个js的子集进行重写改造, 有着优秀的语法设计并且可以编译出经过性能优化的js代码. 可以说, 它和ts属于2个赛道, 但是做的事情都是一样的, 即在开发中帮助程序员消除js的各种陷阱&添加类型.

为什么要学习编译器和解释器?

  • 有意义, 用自己的编程语言去写东西是一个很快乐的事情
  • 我们熟悉的编程软件, 比如js, 尽管可能需要了解js的全貌很难, 但是我们可以尝试去了解一个语言的最小实现, 对于学习语言底层会有更帮助一些; 其次我们可以了解每种语言的抽象机制, 即cost, 每个语言都有自己的擅长领域, 比如有些语言号称zero runtime cost, 但是它相对在编译中就会产生大量的cost, 比如js就是典型的需要在runtime中有大量cost的语言, 那么我们学习不同的抽象机制, 在遇到js相关疑难杂症, 会更容易我们调试, 比如经典的闭包内存泄漏等等…
  • 尝试写一些dsl, 比如vue的sfc就是典型的dsl (或者sql), 通过编译技术将模板编译为可供render的数据结构
  • 提升个人品味 (装逼)

编译管道

Untitled 1.png

  • 从一个字符串
  • 到抽象语法树
  • 经过类型检查到类型抽象语法树
  • 得到多层的IR (中间表示)
  • 线性化之后得到一个更低层次的IR
  • 代码生成得到机器码

在大家的印象中, 编译器的后端部分都比较重, 反而前端比较“简单”, 其实对于现代语言, 反而前端/中端更重要, 要做更多创新和类型检查, 反而后端变得可以重用了, 压力也很小, 因为可以用LLVM去处理IR, 生成的代码也很高效, 而且容易扩展

后端不“重”指的是现代大多数场景, 如果对于一些计算密集或者神经网络加速的, 会对后端要求比较高

Untitled.png

区别几个重要概念

  • 编译, 离线的, 在程序运行之前称之为(预)编译时
  • (解释)运行, 在线的, 每个程序最终都会有运行时. 包括c/c++, 只不过是在cpu执行的
  • 转译器, 从a到b, 它们之前是很相似的, 一般称之为转译

词法解析/语法解析 (前端部分 )

我们从一个简单的文本内容开始, 通过解析会得到一个抽象语法树, 这个是大致的流程; 在这个流程中, 会把文本内容拆解成一个又一个token, 然后会将一个个token再解析为一个语法句子( 比如空白字符, 注释等等…), 然后就得到了一个抽象语法树.

Untitled 2.png

语法对于语义影响很有限, 但是语法对于用户体验影响很大, 本质上我们只要确定了抽象语法树, 语法的设计只要符合规则即可

语义分析/解析

语义分析是在语法解析之后的步骤, 它依赖于ast, 目的很明确: 确定代码含义; 并且为每个语法带来更多的标注工作, 比如类型检查, 作用域, 在语义分析的步骤中, 可能会导致一些常见的错误

  • 类型不正确
  • 使用了未定义的变量

在导致一些错误之后, 也会给这个ast添加更多有用的信息, 为下一步处理做准备.

在这里需要提到一个专业名词叫做: type soundness ,指的就是类型系统中的一个性质, 表达了代码中如果存在类型, 那么在运行时就会严格按照类型运行, 不会出现任何错误, 实现了type soundness的语言也很多, 比如ocaml/rescript

我作为一个java黑, java一直以来都有争议说java是type safe, 但是它不属于type sound, 因为如果类型不一样, 它会直接抛出错误, 但是就算再差也比js好.

针对语言的优化

  • 模块
  • 面向对象
  • 模式匹配
  • class
  • 其他优化
  • 多层/单层的IR转译

线性的优化

cpu是一个流水线的架构, 只能理解线性的东西, 所以我们要有一个线性的IR, 这一层主要就是做IR优化, 比如

  • 常数折叠
  • 常数传播
  • …等等编译器优化技术

针对每个平台进行代码生成

首先每个平台的ISA(指令集架构) 都不一样, 那么这里最重要的一个优化点就是寄存器分配 ,这样做的目的很简单, 就是将我们常用到的变量都从栈上划分到寄存器上, 这样可以更快的执行, 可以更多的利用计算机资源.

因为寄存器在cpu之上, 由cpu直接控制, 所以比内存通信快, 但是它比内存容量要小, 所以要在程序的不同的地方进行寄存器共享, 这就需要编译器在编译期间进行寄存器分配

我们一般讲栈式虚拟机的时候, 是要把变量放在栈上的, 但是由于栈上的数据访问速度要低于寄存器, 所以我们需要把存储数据放到寄存器中即栈分配到寄存器分配, 这也是我们语言的一个优化; 对于编译器而言, 我们做了寄存器分配, 由于可以安全无冲突的共享数据, 也是编译性能的一个优化.

寄存器分配完成之后, 会做一些指令的调度, 针对不同机器相关优化, 比如说经典的后端优化技术peephole

它通过检查一小段指令序列来查找可以优化的机会, 达到更好的执行效率以及减少代码

之前提到过, 在绝大部分场景下, 编译器的后端技术其实并不需要太多的优化, 除非是神经网络涉及一些计算, 在我们熟知的go语言这一类的编译器, 其实后端并没做太多优化, 因为只要保证语言语义足够静态, 在编写编译器的时候, 又尽可能的用上优化手段, 其实都大差不差.

抽象语法 & 具体语法的区别

现在的编程语言解析过程中都不会牵扯到语义, 抽象语法可以一对多的反射到不同的具体语法的.

抽象语法指的是语言的理论结构, 指的是语句的形式和意义结构, 而具体语法就是我们实际编写的语法. 总之抽象语法是建立在语言基本规则之上, 是一种理论, 而具体语法只是一种用法和实现

比如说git就是内核实现了一套抽象语法, 有很多图形界面工具就实现了一个具体用法, 实际上操作的还是git的抽象语法