比尔盖茨与我(上)

比尔盖茨与我

这个标题可能哗众取宠,把一个传奇的人物和我并列,然而我并不是这个用意,我只是想表达比尔盖茨影响我的兴趣和职业选择是深远的。比尔盖茨(Bill Gates)2025年2月发布他的自传《源代码》,喜欢读名人自传的我第一时间拜读了这本自传。在写这篇博客的时候,我刚把它读完,就想着要写点什么。所以我计划写两篇博客,上篇主要说的是盖茨对我的影响,下篇会说说《源代码》的精华内容和读后感。不同于我其他的博客,这次会用中文写作。这样做的目的是为了更多的人可以读到这系列的博客文字,尤其是当下编程教育的兴起和入门年龄下降,很多小朋友已经上上了编程班。即使没有上到编程班的,如今信息化的世代,小朋友或多或少都已经掌握一定程度的IT知识,这是我小时候想象不到的,更不用说1955年出生的比尔盖茨,当时个人计算机,互联网都没有被发明。还有一类读者可能是计划往编程方向鸡娃的焦虑父母,也可以不妨读一读。比尔这本自传是从他出生到他开始学习编程到他和小伙伴在个人电脑诞生之际初创微软的时间线,他在书末写这是一套三部曲。第一部尤其适合当下卷编程的风潮,可以借鉴一下大佬是怎样练成的。

所以,成功者的故事总是具说服力的。不妨买一本《源代码》中文版,好书不贵。

Bill Gates, Source Code Cover

刺激90

我猜我入坑程序编程大概始于上个世纪90年代。

上个世纪90年代,是我小学到初中的阶段,也是中国改革开放进入高潮的时代。尤其在中国南方的大城市广州土著,得益于靠近香港的优势,我很早(具体我也没有记忆了,大概3,4岁)就拥有了主机–是我爸从香港淘回来的N手雅达利(Atari)和一堆生锈的卡带。当时Atari早就在80年代输掉了游戏主机战争,实际上日本任天堂(Nintendo)正在垄断了游戏主机的全球市场。当然当时的我们无论成人还是小孩都不清楚这段历史,改革开放以前数字化或者说IT产品和技术对于一般中国老百姓来说是空白的。这也是我后来从游戏杂志中知道。You know what,90年代末我们也有了我们的游戏杂志了。

反正Atari也是非常好玩的,我依稀记得我玩过Pong和俗称小蜜蜂(其实它叫太空侵略者,只是长得像小蜜蜂,我们也没有人会英语)的射击游戏。

Atari
Game: Pong
Game: Space Invader

我们正在超车中……

到了我上小学的时候,不知不觉间学校周围出现了很多街机店和贩卖游戏机和卡带的店。估计很多人可以从香港搞到走私的游戏机和游戏卡带。那时候最流行当然是日本任天堂的FC(FamiCom,因为它本体颜色是红色和白色,俗称红白机)。其实FC在全球早已退出历史了,90年代中期正是任天堂的SFC(Super FamiCom,俗称超任)和世嘉MD(Mega Drive,那时候大多数店主不会英语,都把它俗称世嘉)激烈抢占市场的时候,当然那是后话。

FC那时候已经拥有超级超级多的非常棒的作品了。现在大多数的游戏类型都是那时候给定义下来。周围的同学也开始拥有了自己的FC了,那时候大概主机200,300左右,一块卡带50左右,有些卡带塞了上百只小游戏,有些卡带只塞一只游戏也卖个100来块,那时候可以说是巨款了。不知道出于什么原因,可能我上学成绩还过得去吧,还是父母以防我跑去龙蛇混杂的街机厅,至少他们现在可以守着电视控制我的打机时间,我也拥有了我的FC,还慢慢地购置了我的卡带库。或许是通关了拿去和同学朋友交换,抑或是去游戏店卖了旧卡补贴一点红包钱(我小学很早就授权自己管理自己的红包钱了),那时候我玩了特别多的游戏,还和朋友同学一碰头就某个人的家玩FC,带上自己的卡带库,可以说是沉迷游戏不能自拔。

FC
Game: Contra

我是直接跳过了任天堂的SFC时代。不知道是家里闲钱多了,还是奖励我读上初中重点班,还是想把我玩游戏的使用权牢牢控制死在必须接电视的主机上,初中我家给我资金去买次世代主机了。那时候中国真的这次实实在在追上了世界。次世代主机的任天堂的N64, 索尼的PlayStation,世嘉的Saturn三足鼎立在我周围的游戏店都有得卖。而且我们可以用远远低于正版游戏的成本买到盗版游戏,一开始20块一片光盘,比FC的动则100以上的卡带便宜多了。而且新游戏的发售时间跟日本正版是同步,我还记得甚至当年Capcom的生化危机2,中国的盗版比官方的正版还早一天发售的离谱事件发生。

那时候我大概花了2500+买了世嘉的Saturn,PlayStation略略贵了一点没有选。后来Saturn都是格斗游戏而且3D游戏成了主导,慢慢游戏都跑去PlayStation发售了,我就把Saturn,盗版光盘,FC的卡带库存卖掉换了PlayStation。PS游戏越出越多,光盘的价格从20跌到10跌到5块,家里的光盘可以用一个大箱子装满(N64是不会考虑的因为是卡带没有盗版)

Sega Saturn
Sony PlayStation

个人电脑时代

游戏主机并不是本篇的主题,跑题跑远了。但是我只是想借这个时代背景来说明

  • 90年代我们已经开始和世界的IT/信息科技/电子消费产品接轨的时代
  • 对于我来说,玩电子游戏,玩大量的电子游戏可能是引领我入坑编程的因素。人都是喜欢玩游戏的吧,大多数。

以前的我常常在想,究竟是怎么造出这么好玩的游戏,到底要学什么东西(学校教的东西好像毫无关系,而且老师经常到游戏店逮学生,好像想这方面的事情并不是正经人该做的)。能解答这个问题,那时候的信息来源凤毛麟角。不过相关的杂志开始普及,这些都是我经常买的(我家里对于我可以阅读的东西容忍度很高从杂志报纸漫画小说练习册都可以拿到钱去买)

DZ Magazine
Newspaper

在IT信息匮乏的整个中国90年代,对于给人电脑的了解就是这些报纸杂志,幸好还能看到香港电视TVB有时看到电脑的新闻,上初中的我了解到还有一种更强大的“游戏机”叫电脑,叫586,686等等。有些先富起来的的同学家里已经买了一台电脑,一问价钱都相当离谱8000到10000DIY一台电脑。相对于次世代的游戏主机3000以下的价钱,个人电脑(PC)堪称巨款。同时间,在那些龙蛇混杂的城中村里面,已经出现很多网吧,在那里我们可以通过局域网联机玩到电脑游戏和上网(一开始还没有上网的)。我并不是网吧的常客,原因很多,一个是联机一次的费用就可以买一块主机光盘,90年代城中村的环境也非常堪忧,正如我不太去街机厅一样,再者老师们最喜欢到网吧去抓捕学生,我并不喜欢这种偷摸的感觉。我最记得我的数学老师在家长会再三重复,电脑是洪水猛兽,买了的话你家的儿子/女儿基本上人就废了。因此,整个初中阶段我没有拥有我的电脑,直到高一才买到我第一台电脑,但是一接触我就会使用了。

Bill Gates

我还记得我的电脑的配置

  • CPU是因特尔赛扬300A(因特尔的次级便宜CPU,买不起奔腾3)
  • 主板是微星的,那时候还需要再买外置声卡,家里上网还没普及,没买网卡
  • 显卡是Nvida的TNT2
  • 内存是128MB(当下的内存是流行32GB)
  • 一台CRT显示器,杂七杂八的鼠标键盘音箱光驱都可以巨大溢价卖出(曾经的装电脑市场是暴利骗小白的)

总计下来应该花了7000-8000。当时没有想太多,电脑当然是被视作超级游戏主机。高一时拿到自己的电脑的时候,其实我已经从邻居和同学家里学了多年很多电脑的操作,启动,装游戏甚至重装系统。在那个时候,在美国,个人电脑大战从70年代开始到80年代末已经尘埃落定,微软(Microsoft),因特尔(Intel)成了最终胜利者。他们一起叫Wintel联盟,微软的操作系统配因特尔的CPU,到90年代2000年的时候我组装的电脑都是他们的架构。

电脑游戏比游戏主机的游戏是碾压的,过去也是现在也是。电脑游戏轻松达到640x480px,然后800x600px到后来1024x768px配搭32bit色深,而次时代游戏确只有320x240px的解释度16bit色深,主机游戏粗糙不堪,棱角分明。电脑再配搭显卡(又叫3D加速卡),把3D图形渲染分配到一块专门的硬件去处理,而主机只有那可怜的3D能力硬从CPU上挤牙膏,所以主机游戏满屏马赛克乱飞。最要命的是电脑硬件的扩展性,你可以更换更好的硬件,然后再拉高游戏的质量,相反主机和主机游戏,为了保证最好的适配,都是固定的主机硬件和游戏设置不能再改变直到下一代主机发售(类似FC到SFC到N64的换代)。

我是从邻居一起打游戏的小伙伴那里学电脑的。所谓学电脑,大概分硬件和软件。硬件的话,那时候兴起的电脑城和DIY热潮,男孩去游玩的大概率就是电脑城买游戏,买卖硬件。

TaiPingYang

所谓的学软件就是微软的操作系统MS-DOS, Windows3.1, Windows95, Windows98,Windows ME, Windows 2000, Windows XP和基于Windows API开发的桌面应用(互联网应用在中国快要到来,那是2000年后的事了)。我还记得MS-DOS的一些操作系统命令,还是用来安装和启动游戏的

cd 目录
# 返回上一级
cd ..
# 列出文件
dir . /w
# 找扩展名是.bat, .com的文件跑就可能是安装或者启动游戏了

这些知识大多数都是在男孩之间口口相传或者在网吧学到或者在电脑杂志报纸学到的,那个时候并没有系统的学习途径。到后来Windows的传播到来都用上了图形界面,那就变得更好玩了,找.exe, .msi,压缩,设置监视操作系统内存CPU,搞硬件CPU超频,杀进程升级BIOS,改注册表,装驱动,中毒杀毒,格式化分区最后重装Windows。那时候Windows的稳定性也是很堪忧的,蓝屏成了广为人知的梗, 使用时间长了或者乱搞软硬件的话会导致不明原因的崩溃,最后手段就是重装系统了。重装Windows的次数之多可以说把重装的步骤刻在了基因上面了。不知不觉间,这些每日实践实际上渐渐地建立了一个非常牢固的计算机组成原理基础。即电脑的主要三大块CPU,内存,IO。软件是操作硬件的。操作系统是最大的软件,隔离在应用软件和硬件之间的最大软件,它需要BIOS开机从硬盘引导到内存等等等等

1995年,我记得那天TVB晚间新闻报道,在美国Windows 95正式发售。美国人彻夜排队为了买到第一份的Windows。那年,那个男人,Bill Gates比尔盖茨,微软的联合创始人,登顶世界首富并且之后多年蝉联。而且他的故事也是非常具戏剧性:中学自学编程,哈佛退学创立微软,偷乔布斯(Steve Jobs)图形界面的点子毁了Apple的个人电脑业务(这是后来乔布斯再次崛起看他们两个相杀的电影硅谷传奇 1999年的电影知道的)。幼稚的我,很容易被这种英雄主义,资本主义影响,甚至我对微软的规模,多少人才能开发出Windows(据报道2000人以上)都归结为盖茨一个人或者他带领几个人搞出这么一个杰作,能运行游戏还能让别人开发游戏和软件听歌看电影上网实在太cool了。而且我那时候对教育体制和老师产生厌恶情绪(或者现在也是),因为被老师在街机厅逮住过和因为他们让我晚了3年用上电脑的厌恶,哈佛退学这种事迹的真的很鼓舞,就像一个抵制垃圾教育的英雄。

我开始寻找怎么学在Windows开发游戏/编程的方法…

阿云立志传

为了达到目的,我陷入到乱买书,乱学各种软件的矛盾中。我买了很多的计算机和学软件的书,但是当时没有一本是好的。那时候引进的图书还不算流行,剩下的都是国内先接触IT的作者二手知识写的书了,写得非常混乱。因为有盗版的光盘,除了电脑游戏我还去淘一些我以为是跟游戏开发相关的行业软件玩,我玩过Photoshop, AutoCAD, 3DS MAX等等等等。后来我查到编程是游戏开发最核心的一个,而且比尔就是写代码的,我就找当时Windows上最流行的编程软件Visual Studio 6.0,买了回来安装。

VS 6.0是当时微软对开发人员的拳头产品了。其实它包含了Visual Basic和Visual C++。VB其实就是用Basic语言开发窗口图形界面的应用了,我读过一些山寨的比尔盖茨自传(山寨的,他2025年才发布自传),微软就是在个人电脑开发Basic语言发家的(到了2025年,尽管Basic语言几乎没有什么人用,但是微软还在最新的Visual Studio里包含Basic的支持)。VB确实傻瓜式(看起来),所见即所得,拖拽控件就能做出好看的界面了,但是缺乏计算机基础的我也止步在这里了,另外我不喜欢Basic语言的语法。

另外一个就是大名鼎鼎的C++了。我当时C++和Visual C++其实是两回事,甚者其实目前C++11以前和以后都差异极大,Windows的C++和Linux的C++也是不同的。C++是一个国际标准组织管治下的编程语言,VC++其实就是微软对C++在Windows的魔改版,魔改的部分就是MFC(Microsoft Foundation Class Library)。当时,VC的难度是我在VB轻松写出来的界面应用我用VC也做不出来,这不是给傻瓜的用户了,就很是沮丧。

我买了很多C++的书。但是就像上面说的,我那时候不清楚C++的这些细枝末节,我买的书有些是VC++的,有些是标准C++的,有些是乱写的,读完一堆堆杂七杂八的书以后更加迷糊(其实这也是C++延伸到当下的现象,也是他被称为难,恶心的一个原因)。后来的后来,我又知道C和C++的那层关系,C是1972年贝尔实验室Dennis Ritchie设计的一个语言,而C++则同是贝尔实验室的Bjarne Stroustrup在1982年设计的一个语言,它最初是给C语言增加面向对象特性的,C++里可以写C的代码。那就是C从某个角度看都是C++的爸爸。学习的一个原则(我自创的):如果你学不懂一个东西,就从它最初代学起。这个套用别的东西,你不懂电视剧的剧情就从第一集看起。那我又买C的书,正好买了这本谭浩强写的C语言程序设计,正好我的大一学的C语言也是用这本,基本上国内的高校计算机系都会用它

C Book

我的忠告是学C语言千万千万千万别读这本书!大学里我看过多少本来还有一点兴趣学习编程的同学最后都没法学下去就是因为这本书。它的内容过时,毫无使用性,玩花活,你认真学完可能会玩千万种C语言的花活杂技,但是你绝对写不出一个像样的C程序。如果你的学校发你一本这样的书或者你被推荐买了它,放下它别读,当下其实有很多好的C语言书籍无论国内还是国外作者。关于这个也是可以在别的博客再说。

在缺乏好书和0引路人的情况下,加上高考学习压力非常巨大,除了应付那场决定人生的大考和必要的生存活动外,几乎没有时间碰电脑,游戏等等了。显然我并不具备比尔盖茨退学哈佛的勇气和决心,我打的是安全牌,甚至我幻想上了大学读计算机系的话,那里的老师会是好的引路人,因为我当时确实没有其他途径了(和当下完全不同的,你完全可以自学)。我有且仅有一条路,考上本科,报读计算机专业。

那场大考前,我的志愿单很简单几分钟搞店,全部学校填满,第一志愿都是填:计算机科学与技术

Don't Use Sequence for Incremental ID

I learn from work about the relational database design. For many of providers of relational db product, they offer the sequence building block that uses SQL to call something like nextval to generate next integer value in each call. It’s tempting to use the sequence generated value as the primary key of a table. However I was told not to do this for the main reason:

Sequence is not a proper DB object that could not be migrated when DB failover. That means the sequence is reset to the initial value so that you will lose the latest ID number if failover. Although you can write the SQL script to resolve the latest number from table then recreate the sequence. But imagine that in an urgent failover case, you would not hope to do so.

Somebody advices to use a table to replace sequence. The pseudocode looks like that.

An ID table:

typemax_id
type1199
type2200
….

And sequence can guarantee the atomicity, that’s why many people like to use that in microservices architecture but one single DB. And sequence can handle well the isolation where multiple services are getting ID from DB. If we won’t use sequence, then we need a function that can do transactionally like sequence’s nextval

FUNCTION nextid (type)
    BEGIN TRAN
        select max_id from ID_TABLE where type = type
        update max_id + 1
    COMMIT TRAN

Then we can use this function in code to replace nextval

INSERT INTO some_table where nextid(some type)

(Above are pseudocode that demonstrates the idea…)

React's Nature Is Asynchronous

Here is the thing, React is a WTF framework that has a steep learning curve yet powerful in the frontend development. Last time, I have written about this blog about react hooks. The more I use react in my day job projects, the more I can dig out new to refresh my perspective.

The problem in this time makes me conclude that react itself is almost asynchronous in every aspect and in its utilities and its design idea.

My goal is simpler. I work in the ticket context which has many states (useState hook) and callbacks (useEffect hook) are exposed for its potential consumers. And the context also has a bunch of effects (useEffect hook). As far as I know useEffect hook is used for kind of things like subscribers to states, lifecycle functions on a react component/context. Back to my goal, I want to have boolean flag that can indicate whether a callback is called, then set to true, otherwise it is false initially and the context somehow is reset. So I create something like

const [flag, setFlag] = useState(false);

// In callback is called
...
setFlag(true);
...

// In a useEffect hook that does the reset I think of
...
setFlag(false)
...

// Somewhere in the code I want to test the flag value to do thing
...
if (flag) {
    // Do something when flag === true
} else{
    // Do something else when flag === false
}

Battling with React

However, I can see the inconsistent outcome where the setFlag(true) does run, but the flag is not set to true in the if-else block. Worse thing is that when I click more times from UI, probably the second time or the third time, flag is set to true. But that has already messed up the audit logs etc. I can’t accept this as a done.

I guess that incorrectly set the deps array of useEffect hook can lead the callback to be triggered more than you want (or less than you want). I am trying to tweak multiple combination of the state inside/outside of the deps array of the useEffect hook, or in another new useEffect hook etc. TBH, I can’t quite remember the combinations I have tried. At least I can conclude that the elements in the deps array is OR relationship.

It’s painful to deal with this kind of issue. I begin to question Gihub Copilot from the other angle because the chat suggests me to think useEffect and useState direction (I know that the AI mislead me now). I try to ask it that “I want the state change to be synchronous for example, setFlag(true), the if (flag) the flag must be true sth like that”. I’ve known React could set the state in its painting lifecycle, which is hidden from us and is only controlled by React rendering engine. Surprisingly, the Copilot this time suggests me to try useRef, which it says useRef is synchronous as I wish. Hence, I replace the useState with useRef, abandon mutation of true/false in the useEffect hook. Finally that works!

I’ve Learned

useState is asynchronous and controlled by the React internal painting engine (maybe it is called like that). The state value is not deterministic where it could be changed to something else by React cycle runs, or your expected value is not yet evaluated while the React cycle does not yet run. See its caveats. Some key points: only update in next render, use Object.is to determine state change or not, React batches state updates, which means the state could possibly not be updated in time balabalabala…

useRef that lets you reference a value that’s not needed for rendering. The very first statement in the doc is already promising. See its caveats. Strictly speaking, useRef is not simply considered as synchronous, instead useRef is excluded from React’s rendering and React acts asynchronously, that makes useRef acts in sync manner or suitable in sync scenario. You can get what you change if using useRef.

One caveat I learnt from Copilot is that if you want to mutate the state behavior out of the React rendering cycle, you can move out state’s value to useRef.

WTF React

A few blames I can moan:

  • The name useState and useRef do not convey one can be affected by the React rendering but one not.
  • Should I read all React’s document? They are a lot…
  • Probably I could be more sensitive because get value from useState and useRef is different where aRef.current if using useRef. That warns you they are different.
  • Framework is also a double-edged thing. React wants to internally do more optimization for developers so it has to hide things for you with some poor naming hooks.
  • Svelte may implement this functionality better. Like reactive statement $: then you know it is doing non-deterministic thing out there.

React's useEffect Hook Gets Called Twice

Side Effect Icon

I find myself to be confused while using React’s useEffect hook often. During recent work, I need to use useEffect hook to observe a state or (some state) changes and perform certain code. However, it always results unexpectedly due to extra invocations of that certain useEffect hook, which then calls the side-effect calls ot APIs multiple times and causes incorrect state. I am fighting against the useEffect hook until the Deepseek suggests me setup the deps to a callback function of useCallback hook. However, I am still confused so I try to summarize the workable practices on useEffect hook in this blog.

The Deepseek tells me that useEffect hook can be invoked twice intentionally in dev mode in strict mode. That can explain why I see excessive calls on useEffect hook. Because React components will mount twice in dev mode in order to help developers to find out potential issues (Jesus…).

On the other hand, the suggestion advises me to set deps array to a useCallback so that unnecessary useEffect calls.

So probably I do in this way

const aCallback = useCallback(() => {
    ......
}, []); // Skip the deps of useCallback so that the callback is just cached one time

useEffect(() => {
    ......
}, [aCallback]);

By doing so, if the useEffect hook is invoked twice in dev mode, which implies okay because this behavior is expected in React. This helps to rule out the issue by starting deps simple.

Honestly, the way of React to observe reactive state is clumsy and error-prone. I don’t like React.

Using yazi as File Manager

Duck Toy Icon

What is yazi

Yazi claims itself as a blazing fast terminal file written in Rust. In Linux, doing everything in terminal always make hackers happier. Compared to Windows, Linux ecosystem has abundant command line tools, which increasingly improve the productivity. Also staying fingers on the terminal and using less mouse can improve the productivity when typing things is the most common tasks for a hacker.

Install yazi

I am using homebrew as my package manager in Linux, so simply just input this command in terminal

brew install yazi

Use yazi

After installed, run this in terminal

yazi

Then yazi takes the control of terminal and displays the TUI.

Run Yazi

Use keyboard to do the regular movement just like other GUI file managers. The good thing is that the default yazi setup configures a 3-sections layout that is like a IDE layout. The first column is the folder tree, the 2nd column is the file view, the leftmost column immediately display the file content when navigating them. Surprisingly, it has the code highlights. Cool!

And this is the first short blog about yazi installation and brief usage. I will explore more features and write new blogs about yazi.

Happy coding.