MoreRSS

site iconBryan修改

英文昵称 Singee,INTJ,经济学+法学双专业,产品经理、工程师、投资人。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Bryan的 RSS 预览

浏览器从 A 到 Z

2025-03-21 07:57:06

将 A-Z 逐一输入到 Google Chrome 的地址栏里,我的 Google Chrome 都会自动补全出哪些域名呢?

本文灵感来源于 2024: 浏览器从 A 到 Z,首发于少数派

A - https://axiom.co

Axiom 是我最喜欢的网站日志收集分析工具,免费版拥有着高达 500GB / 月的免费额度,我的多款产品(包括我最新正在做的 1Space)都是使用的它作为日志收集。

如果你在找一款日志工具,强烈推荐它!

B - https://baidu.com

无可争议…… 虽然我现在确实不怎么用百度了,但是网络连不上的时候还是第一时间用百度做测试的😂

C - https://chatgpt.com

ChatGPT 作为 AI 时代的先驱,在目前似乎仍然保持着第一🤔

如果这篇文章是在 2024 年写的,那么我的 C 可能就不是它了;2024 年或许算是 ChatGPT 落后的一年,但是在 2025 年,O1-Pro 和 Deep Research 让它再次成为了模型领域的 No.1。我追随着最新的前沿技术,也订阅了高达 $200 / 月的 ChatGPT Pro,或许很贵,但我觉得它确确实实为我节省了很多的时间和精力。

D - https://discord.com

论「社区」,似乎已经有越来越多的服务选择在 Discord 这个平台上建立了。我在用的很多产品都用它作为产品发布和讨论的渠道。

E - edge://inspect

没想到竟然是个 Inspect 页面。这是 Chrome 自带的浏览器调试工具页面(不过因为我在用的是 Edge,所以不是 chrome://inspect 而是 edge://inspect)。我在做的 1Space,因为利用了 Shared Workers 作为多标签页的同步方案,因此我需要频繁访问它来查看同步日志。

F - https://fly.io

Fly IO 是我用了数年的容器服务平台,曾经驱动了我绝大多数的产品(其实现在也有不少,还剩下 40% 吧),体验很不错。

Fly IO 给我印象最深刻的其实是他们的 招聘。他们的招聘与绝大多数的面试不同,采用的是「做 2-3 道实践题」+「与他们工作一天」的形式,只看能力,不看背景(甚至他们不要求简历),而且是全远程工作、薪资透明(仅与面试定级有关,与历史薪资、所在地域无关)。

G - https://gmail.com

Gmail —— 一个不存在的邮箱平台

H - http://localhost:3333

好好好,竟然是 localhost —— 3333 这个端口是我的 1Space 的本地开发服务器所使用的端口。

I - https://inoreader.com

虽然 RSS 越来越没落,虽然 RSS 平台越来越多(嗯?这俩之间的因果关系有点反直觉),但我还是觉得 RSS 是最适合我的信息收集渠道,inoreader 也是最好用的 RSS 客户端。

多说一句(广告时间到),inoreader 单纯做 RSS 已经到了几乎极致了,但是我们用 RSS 的目的或许不是为了收集信息,而是为了学习信息,而在整理回顾上,inoreader 就没那么强了,甚至我觉得其他能作为它下游知识管理的产品(例如我目前在用 readwise,或许下面 R 能看到它的身影)也不够优秀。因此我做的 1Space 目的就是打通知识「收集 - 管理 - 回顾」的全流程。

J - https://www.jetbrains.com

JetBrains 曾经是无可争议的 IDE 老大。

唉,曾经。JetBrains 的体验真的比 VS Code 好太多,哪怕在 VS Code 最擅长的前端领域,我也敢说 WebStorm 吊打它。奈何,现在是 AI 的时代,IDE 已经被 Cursor 为首的 AI IDE 重塑,而 JetBrains 真的在 AI 的潮流上落后了。打败你的,可能并不是你的竞争对手。

K - https://kb.singee.me

哦吼,我自己的 Knowledge Base!我的知识库一直是公开的,我的很多懒得不适合整理成博客的内容都写在了上面,主要记录我各种笔记、踩过的各种坑。

L - http://localhost:3333

嗯…… 和 H 一样,看出来我真的很努力去做 1Space 了。

M - https://monica.im

在我从浏览器中测试「M」之前,我就猜到了,我最常用的网站必有它。

Monica 可能宣传的不多,但是说它所属公司的另一个产品 Manus 估计大多数人都听过。如果说 Manus 是 AI Agent、完全替代人工方面的王炸,那么 Monica 就是你日常使用 AI 过程中的瑞士军刀 —— 你需要的 AI 能力,几乎总能在 Monica 中找到。

N - https://notion.so

竟然是 Notion。Notion 似乎我已经不用多余篇幅介绍了,估计能看到本文的受众都见过它。我曾经是 Notion 最早期的用户之一,但坦白说我已经挺长时间没怎么用过 Notion 了…… 其他 N 开头的产品还不够能打哇🤷

O - https://originui.com

又是一个程序员专属产品。这是一个 UI 组件库,算是 shadcn 的补充,同样以「复制-粘贴」的形式引入组件,需要的人值得一看!

P - https://www.paypal.com

竟然是 Paypal,类似国内的支付宝的产品?又是一个实际上我没怎么用的产品,看起来 P 开头的服务也不够能打😮‍💨

Q - 不便透露

emm 企业内部平台,略过

R - https://read.readwise.io

哦!Readwise Reader!一个稍后读的阅读器 + RSS!

真的挺好用的,而且对于我一个早期的 Readwise 用户而言直接是加量不加价。但我其实对它的很多细节还是不怎么满意的。我的 1Space 去年才开始正式做,但其实早在数年前就有计划了。但当时,刚刚打算做,就遇到了 Readwise Reader 宣布立项,我被他们开始宣传的「Reader for Power Reader」吸引了,决定等他们的产品,奈何,等了这么久,虽然已经比其他阅读器做的好很多了,还是不足以达到我想要的地步😮‍💨 最后还是没逃开自己做的命运。

S - 不便透露

我的某产品的内测页面🤔

T - temporal-web.temporal.svc.cluster.local:8080

Temporal 的管理面板。Temporal 是一个 Workflow 管理与调度的工具。写后端的人应该都知道,如果一个逻辑直接使用很普通的代码编写,在项目发展的过程中,很容易就会遇到复杂度指数级提升。如果并发、重试等基础操作在每个接口、每个 RPC 调用都自己写一遍,实在是没有意义,而且很容易漏了哪里导致上线 bomb。Workflow 就是对这种场景的一个解决方案,你的一切逻辑都定义成 Workflow,而重试、日志、并发等等都由调度器管理,项目初期看起来可能稍显繁琐,但是随着项目复杂度的提升、随着项目对并发的要求的提升,你会感谢当年选型选择了使用 Workflow 进行组织的你的。

哦对,Temporal 被设计为可以支撑超大型项目。如果你的项目是个中小型项目,也可以看看其他解决方案,例如 Trigger.devinngestRestate

U - https://ui.shadcn.com

要不是这篇文章严格按照字母序组织,它应该是和前面的 Origin UI 放在一起的。Shadcn/ui 简直就是没有设计能力的开发者的福音,如果你想做一个自己的产品,又苦于不知如何让页面变得好看,不如试试它。

另外,shadcn/ui 的开发者目前已经加入了 Vercel,因此 Vercel 的 v0 对 shadcn/ui 有很强的支持,如果你不但没有设计能力、甚至不是开发者,那么,利用 v0,只动动嘴皮子也可以得到利用 shadcn/ui 组织的很好看的界面。

V - https://v2ex.com

i2exv2ex 论坛可以说是中国最大的同性交友社区程序员论坛了,嗯,就是这样。

W - 不便透露

我自用的某产品页面

X - https://x.com

全球最大的社交平台,实话说,对不追星的人来说,比微博好玩多了

Y - https://www.youdao.com

各种词典软件层出不穷,但我觉得依然还是有道最好用。带有韦氏、柯林斯、牛津的资源,各种原版例句,而且完全免费。

Z - https://zeabur.com

如果你是一个开发者,又不想浪费过多的精力在自动部署、运维上,就选 Zeabur 吧!推送代码秒部署,最重要的是,还有国内的服务器节点,真的很好用

使用 TypeScript 撰写 OmniFocus 脚本

2023-12-13 10:08:49

OmniFocus 4 即将发布!在我多年管理我的待办的过程中,我尝试过 Todoist、滴答清单、Things、Sorted 等等几乎所有市面上的 TODO 软件,但最终,OmniFocus 终成我一直以来的最终选择。而谈及 OmniFocus 的强大性,不得不提的就是他强大的自动化能力 —— Omni Automation

Omni Automation 实际上是基于 JS 脚本的,而编写纯 JS 脚本的过程…… 一言难尽。虽然 Omni Automation 官方提供了 TypeScript 的定义文件,但一方面难以做好类型检查,另一方面其详尽程度仍有待提升(长久不更新、大量使用 any 等),此外,由于缺乏打包工具,代码逻辑的复用也显得颇为困难(我甚至很长一段时间都是靠着 Mac 版本 OmniFocus 的一个 bug 实现的逻辑复用)。

为了庆祝 OmniFocus 4 的面世,我决定将我个人开发并使用的方案整理开源,包括打包脚本和类型定义,还有我使用的一些工具函数及脚本,希望可以让更多人能够愉快地编写 OmniFocus Script。

使用

  1. 使用此模板创建一个仓库
  2. 克隆你创建的仓库
  3. 运行 pnpm install 安装依赖项
  4. 运行 pnpm build 构建脚本

脚本源码放在 src 目录中,编译结果(可被 OmniFocus Scripts 使用的)放在 dist 目录中。

撰写脚本

src 目录内的任何不以 _ 开头的 TypeScript 文件都将被视为 OmniFocus 脚本并编译(_ 开头的脚本文件被保留用于工具函数)。

任何脚本都必须遵循以下模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export const action = new PlugIn.Action(function (selection) {
// do anything you want
});

action.validate = function (selection) {
// do anything you want
};

export const meta: Meta = {
label: "...",
description: "...",
identifier: "...",
author: "...",
version: "0.1",
};

其中:

  1. actionmeta 是必需的,action.validate 是可选的
  2. meta 必须是脚本的最后一部分。它之后不可以有任何内容。

构建与使用

运行 pnpm build,构建后的脚本(以 .omnifocusjs 结尾)将被放置在 dist 目录下。

你可以直接将 dist 目录中的脚本拷贝到 OmniFocus 的脚本目录,也可以利用脚本进行同步。

如果你使用 iCloud 保存 OmniFocus 脚本,可以直接使用 pnpm sync 自动将构建好的脚本同步到 iCloud 中的 OmniFocus 脚本目录;如果你不使用 iCloud 而是使用了自定义路径,可修改 sync.sh 文件改变目标路径。

End

此方案我个人已用一年有余,但一方面开源版本可能有些错误,另一方面可能有更多的定制化需求。

欢迎进入 仓库 页面提交 Issue 和 PR!

[备忘] Go init 行为

2023-12-12 06:29:38

总结

基础规则:

  1. 所有的 init 函数都在一个 Goroutine 中执行(但请参见下面的特殊注意)
  2. 如果 package a 引用了 package b,那么 a 的 init 一定在 b 的 init 运行完成后运行
  3. main package 的 main 函数一定在其他 init 函数均运行完成后再运行(即运行顺序为 package 的 init -> main 的 init -> main 的 main)
  4. 同一 package 中的多个文件中的 init 执行顺序未定义,同一文件中的 init 自上而下运行
  5. 如果 package a 同时引用了 package b 和 c,那么 b 与 c 的 init 顺序在 Go1.21 及之后定义

在 Go1.20 及之前:

  1. 如果 package a 引用了 package b,那么 b 的 init 一定在 a 之前运行
  2. 但是,如果 package a 同时引用了 package b 和 c,只要 b c 之间没有引用关系,b c 的执行顺序是不定的

在 Go1.21 及之后:

  1. 对于无引用关系的包(即 Go1.20 及之前的中的第 2 点),按照其包名字母序决定引用顺序(例如 a 一定在 b 之前执行,github.com/xxx/xxx 一定在 gitlab.com/xxx/xxx 之前执行)

特殊注意:

  1. 如果 init 存在阻塞,那么用于运行 init 的 goroutine 可能创建新的 goroutine,这会导致某些 init 代码并发运行
  2. 存在阻塞的情况下,不会保证无引用关系的 package 的 init 完成先后顺序(参考示例 c)
  3. 存在阻塞的情况下,如果 package a 依赖了 package b,那么 a 的 init 一定在 b 的 init 运行完成后开始运行(参考示例 d)

示例项目

https://github.com/singee-study/go-init

参考

The Go Memory Model
Go 1.21 Release Notes

[随笔] 一开始就公布定价

2023-11-25 17:13:06

看到了 FeedbackTrace 这个项目,看起来不错,但是我不会去使用,一个主要的原因就是它没有公布定价。

如果去选择一个一个直接面向 C 端的第三方产品,我一定会选择已经有了明确定价的 —— 否则我将承担巨大的未来的风险。如果未来定价不满足我的预期,我需要去考虑怎么迁移数据、怎么抹平用户体验差异等,一方面可能带来不确定的时间成本,另一方面可能产生糟糕的终端用户体验。

面对公测期间的价格和上线后不一致的做法,各个产品有各个产品不同的思路,最统一的就是降价后下一期账单自动降价、涨价保持原有价格不变或至少保持原有价格一定时间。特别的,Rewind 在 Early Access 期间的策略是调低定价后为历史订单全额退款、调高价格保持用户的原有价格不变,而 ChatGPT 的策略则是调低定价后给予用户差额价格退款并给予额外补偿。

多说一句,公测免费并不是不预先提供价格点的「借口」,完全可以预先告诉哪些是付费点但是公测期间可以免费使用(例如 Omnivore 明确在文档中说明了它的哪些功能未来可能是收费的)。

因此,我的产品,也一定在最初就给出一个收费点定价方案 —— 哪怕不够完美。大体来说,我会提前将价格和收费点列明,公测期间可以免费使用收费功能(或部分收费功能),而预先开始订阅收费版本的用户(即早期支持者)承诺未来如果存在价格调整他们一定可以「使用最低且不高于现有的价格得到最多的服务」。

[随笔] Server Action

2023-10-27 07:48:02

最近在一个自己的小 Side Project 中使用了一下 Server Action,感受大概是

  1. 更多的其实是替换原来的 /api 路由,可以把它想做一个 TypeSafe 的 RPC,原来需要自己去定义 API 然后生成代码,这些 Server Action 直接帮你做了
  2. 因此,感觉原来用了 Next.js 的 /api 路由的,迁移到 Server Action 会有较大的体验提升
  3. Server Action 本质只能向前端发送一个 JSON Object,缺少了标准 HTTP/RPC 的 StatusCode 和 Headers 的能力,因此如果真的想去很好的用它可能 Server 和 Client 仍然需要包一层
  4. 目前 Server Action 实质上缺少触发前端状态更新的逻辑,如果很需要其实可以利用 WebSocket 或者利用「包的那层」来手搓,实现起来其实也不难
  5. 我遇到 Server Action 最主要的问题其实是它触发调用的那层做的事情实在是太多了,会触发很多 React 内部的状态转换逻辑,另外我发现它与 useReducer 一起用甚至还有 bug(哪怕是目前 Next.js 14 Stable 了)

综上,全栈项目玩玩可以,其余的…没啥必要

[随笔] Swift 异步:Task vs DispatchQueue

2023-09-21 09:31:41

在 Swift 上,执行一个异步的函数大体上有两种办法

  • Task
  • DispatchQueue

背景知识:线程和队列

Swift 同时支持多线程和异步,因此

  • 存在主线程和多个后台线程
  • 每个线程存在若干队列(若干个全局 global 队列(每个优先级一个)、自定义队列(人为创建))

背景知识:队列优先级

在队列层面,存在优先级的概念(在 Task 中叫 priority,在 DispatchQueue 中叫 qos)

  • userInteractive:最高优先级,适用于 UI 操作(例如动画等)(在 Task 中被弃用)
  • userInitiated:较高优先级,适用于用户触发的操作
  • default:中优先级,默认(在 Task 中被弃用)
  • high, medium, low:高中低优先级(仅在 Task 中存在)
  • utility:较低优先级,适用于耗时的后台任务
  • background:最低优先级,适用于耗时的后台任务
  • unspecified:继承于 Thread.current.qualityOfService(在 Task 中被弃用)

背景知识:main

main 同时隐含着两个概念:main thread 和 main queue,事实上无需特意区分(可以认为只有 main thread 才有 main queue,而 main thread 的不同 queue 之间并无特殊区别)

main 的主要用处在于其是直接和用户交互的,只有在 main 才能修改 UI、如果 main 繁忙用户会感觉到 UI 刷新卡顿

  • 对于 Task 而言,在 main 执行使用 Task { @MainActor
  • 对于 DispatchQueue 而言,在 main 执行使用 DispatchQueue.main

注意,Task 的 @MainActor 实际上并不是让闭包代码在 main 执行,而是让其他执行它的 concurrency-aware 代码逻辑在 main 上执行(但是目前存在非 concurrency-aware 的代码,因此可能标记了 @MainActor 实际上仍然是在非 main 执行的,这种时候需要手动切换到 main;不过这种情况 XCode 会有警告,所以不必过于担心)

DispatchQueue 相关

  • 依赖的是 Grand Central Dispatch(GCD) libdispatch
  • Dispatch.main 是在主线程执行的,其他均不保证实际执行线程
  • 执行顺序先进先出
  • 队列类型分为 serial 和 concurrent 两种,serial 在执行完一个任务后才会开始执行另一个、concurrent 会同时执行多个任务(故不保证任务的结束顺序);main 是 serial 类型的、默认也是 serial 类型的
  • 执行时存在 sync 和 async 两种,sync 会等待这个任务完成后返回,async 会直接返回
  • 因上述特性,在 main 线程执行 DispatchQueue.main.sync 会导致死锁
  • 无法直接获得执行结果

Task 相关

  • Task 有 child 和 detached 两种类型,区别在于后者无法访问到调用方可见的变量
  • 来自于 main 创建的 child Task 会始终在 main 执行,否则(除非标记 @MainActor)不保证执行的线程/队列
  • 已提交的任务可取消、可获取(等待)结果(获取结果需要 await)
  • 如果和 DispatchQueue 类比,可以认为 Task 与 DispatchQueue.async 执行上的行为一致

额外:RunLoop

  • RunLoop.current 返回当前的线程循环、.main 返回主线程循环
  • 通常情况下,无需特别注意 RunLoop.main 和 DispatchQueue.main 的区别,二者都是在 main 上执行逻辑
  • RunLoop.main 中执行的逻辑可以被外部用户操作暂停,而 DispatchQueue.main 不会,因此在处理滚动时可能更希望使用 RunLoop.main,而其他通常场景则一般使用 DispatchQueue.main