在云原生大行其道的今天,如果一名架构师说:“我不想用 Kubernetes,我想直接用 Linux Systemd 部署”,很多人可能会觉得他在开倒车。

我们沉迷于 Kubernetes 提供的服务发现、负载均衡和自愈能力;我们习惯于将域名解析、CDN 甚至安全策略全盘托付给 Cloudflare。这些工具像魔法一样掩盖了底层的复杂性。

但这种便利往往伴随着一种隐秘的焦虑:如果这些魔法失效了怎么办?

这不是杞人忧天。当 Cloudflare 发生全球性宕机,控制台无法访问,甚至无法修改 NS 记录将流量切走时,我们除了看着 503 错误发呆,什么也做不了。当 Kubernetes 集群的 etcd 数据损坏且无法恢复,我们是否还有能力在几台裸金属服务器上,仅凭标准 Linux 指令让业务跑起来?

Kubernetes 应该是我们的一个选项,而不应是唯一的生存土壤。 真正的健壮性,来源于应用对环境的“低依赖”以及对原生能力的“高兼容”。

那些让我们陷入“温水煮青蛙”的强依赖

除了 Kubernetes 和 Cloudflare,现代技术栈中还有太多让我们丧失“原生生存能力”的陷阱。我们需要审视以下几类典型的强依赖:

1. Serverless 与 FaaS 的“代码级锁定”

AWS Lambda 或 Vercel Functions 极大地简化了运维,但它们往往要求代码按照特定的 Handler 签名编写,并依赖特定的触发器(Trigger)机制。

  • 风险: 你的代码不再是一个标准的 HTTP Server,而是一个只能在特定云厂商运行时里跑的“碎片”。一旦云厂商涨价或改变策略,迁移成本极高,因为你无法直接将这些碎片扔进一台普通的 Docker 容器里运行。
  • 原生替代思考: 应用应当首先是一个标准的 Web Server(如 Go 的 net/http 或 Python 的 WSGI/ASGI)。FaaS 应该只是最外层的一层薄薄的适配器(Adapter),而不是代码的骨架。

2. 专有数据库服务(Vendor-Specific DBs)

使用 AWS DynamoDB、Google Firestore 或 Azure Cosmos DB 非常诱人,它们提供了无限的扩展性和极简的 API。

  • 风险: 与 MySQL 或 PostgreSQL 这种基于标准协议的开源数据库不同,专有数据库的 API 是独占的。一旦你的业务逻辑深度耦合了 DynamoDB 的 Single Table Design,要想迁移到自建的 SQL 或 NoSQL 环境,基本等同于重写整个数据层。
  • 原生替代思考: 是否设计了良好的 Repository 模式?如果脱离了云厂商的 IAM 认证和专有 API,你的应用还能连接一个本地运行的标准数据库吗?

3. 身份认证即服务(IDaaS)

Auth0、Okta 或 AWS Cognito 接管了我们的用户系统。

  • 风险: 如果服务商封禁了你的账户,或者发生服务中断,你的所有用户瞬间无法登录。更可怕的是,用户数据并不在你手中,导出和迁移用户哈希数据的难度极大。
  • 原生替代思考: 即使使用 IDaaS,是否保留了同步用户数据到本地数据库的能力?应用是否支持标准的 OIDC/OAuth2 协议,以便在紧急情况下切换到开源的 Keycloak 或自研的简单的 JWT 服务?

“原生回退”:不仅仅是备选方案,更是架构试金石

你可能会问:“维护两套完全不同的部署逻辑(如 K8s 和 Systemd),成本不是翻倍了吗?”

恰恰相反,这种差异性正是检验架构解耦程度的最佳试金石。

如果我们强制要求应用必须具备在“原生操作系统”上运行的能力,这会迫使我们思考很多本质问题,从而优化整体架构:

1. 服务的发现(Service Discovery)

  • K8s 依赖: 代码里直接写死 http://my-service,依赖 K8s 的 CoreDNS 和 iptables 转发。
  • 原生思考: 如果没有 K8s DNS 怎么办?这迫使我们在代码层面支持配置化的服务地址。在裸机模式下,它可能是 localhost:8080 或配置在 /etc/hosts 中。应用不应假设网络拓扑,而应通过配置感知拓扑。

2. 配置与密钥管理(Configuration & Secrets)

  • K8s 依赖: 极度依赖 ConfigMap 和 Secret 挂载,甚至依赖 Sidecar 动态注入。
  • 原生思考: 如果没有 Sidecar 怎么办?这促使我们回归标准:环境变量(Environment Variables)。这是最通用的标准,无论是 K8s、Systemd、Docker 还是 Shell 脚本,都原生支持环境变量。

3. 负载均衡与入口(Ingress)

  • Cloudflare/Ingress 依赖: 依赖边缘节点的路由规则和 SSL 卸载。
  • 原生思考: 如果外层壳子没了,我们是否有一个标准的 Nginx 配置文件作为备用?这要求我们的路由规则不能过于依赖特定厂商的非标准语法(如 Cloudflare Workers 里的特殊逻辑),而应尽量保持在 HTTP 协议的标准范畴内。

构建“逃生舱”策略

为了避免“傻眼干等”,我们需要将“备选方案”从文档变成可执行的代码。

1. 定义“最小生存环境”

不需要在备选方案中复刻 K8s 的所有高级功能。我们需要定义一个“最小生存环境(Minimum Viable Environment)”:

  • 计算: 标准 Linux VM 或裸金属服务器。
  • 进程管理: Systemd 或 Supervisord(操作系统原生能力)。
  • 网络: 标准 DNS 解析,Nginx 反向代理。

2. “双模”部署验证

不要让备选方案只停留在理论上。可以采取以下策略:

  • 开发环境原生化: 强制开发人员在本地(非 K8s 环境)直接运行二进制文件或简单容器。如果开发者本地跑不起来,说明对平台依赖过重。
  • 灾备演练: 定期(如每季度)尝试在一个完全隔离的、没有 K8s、没有 Cloudflare 的纯净 Linux 环境中部署全套系统。

3. 基础设施即代码(IaC)的抽象

使用 Terraform 或 Ansible 时,尝试将“应用逻辑”与“基础设施胶水”分离。

  • Plan A (K8s): Terraform -> AWS EKS -> Helm Charts.
  • Plan B (Native): Terraform -> EC2/DigitalOcean Droplet -> Ansible Playbook (安装 Systemd Service)。

回归朴素的技术价值观

我们并不反对 Kubernetes,也不否认 Cloudflare 的伟大。我们反对的是“无意识的依赖”

拥有在原生操作系统中部署运行的能力,是一种底气。这种底气意味着:虽然我选择了云,但我拥有随时下云的自由;虽然我使用了复杂的编排工具,但我从未忘记如何用最基础的命令让代码跑起来。

当我们将应用与基础设施解耦,减少对特定运行时环境的黑盒依赖时,我们得到的不仅是一个可用的灾备方案,更是一个结构清晰、边界明确、生命力顽强的软件系统。

柴锋(Odd-e)

在现代软件工程的复杂世界里,“依赖管理”这个词汇对开发者来说再熟悉不过。我们习惯于在 package.jsonpom.xml 中审视那些明确列出的软件包,认为这就是依赖管理的全部。然而,这仅仅是冰山一角。一个真实的、贯穿始终的故事,将揭示冰山之下那片更广阔、也更危险的水域,它最终将引向一个更深层次的命题:在大规模软件系统中,“信任”究竟从何而来?

一场关于“信任”的三重考验

这个故事,我是亲历者,也是全程的参与者。2018年,一家通信设备公司面临某国政府公开对其产品存在“后门”的尖锐质疑。为了自证清白,公司开启了一场长达两年的马拉松式技术验证,这期间经历了三轮不断升级的严苛考验。这段经历不仅是本文探讨问题的缘起,更是一次宝贵的契机——我与客户方一位核心技术管理者在共同应对挑战时,因对工程哲学的深刻共鸣而开启了此后几年的深度合作研究。故事,就从这里开始。

第一重考验:源码审计

要证明产品安全,最直接的办法就是审查源码。于是,公司将产品的全部源码提交给对方政府,由其联合多家顶级安全与软件公司进行长达半年的逐行审计。最终的结论是:源码是干净的,未发现任何后门。

然而,第一个问题随之而来:源码是安全的,产品就一定安全吗?听起来答案显而易见,但事实远非如此。

第二重考验:构建复现

质疑方很快提出了一个更深入骨髓的问题:“我们承认源码是干净的,但我们部署在网络里的产品,真的是用这份源码构建出来的吗?”

这是一个极为合理的疑问,直指“阴阳源码”的可能性。为了打消顾虑,公司必须在对方完全控制的基础设施中,重现整个构建过程,且最终产物必须与线上部署的产品做到“比特级别一致”,就算有差异,也必须是可以解释的,比如由时间戳、文件路径、排序等引起的差异。这是一个近乎疯狂的任务,因为复现一个规模庞大、技术栈复杂的系统本就困难重重。但经过又一个半年的努力,构建成功复现,证明了产品与源码的一致性。

第三重考验:终极质询

源码可信,产品由源码构建,这下总该放心了。但对方抛出了第三个,也是最致命的问题:“你们怎么证明,在如此复杂的构建过程中,每一个步骤都是安全、可审计的?没有任何机会可以悄无声息地引入风险和后门?”

这个问题直指现代软件工程的核心——生产过程。它不再纠结于可见的源码,而是拷问那些看不见的环节:构建脚本里有没有隐藏任务?构建过程中用到的所有工具,有没有可能故意修改源码或结果?这并非杞人忧天,2024年的 “xz 后门事件” 就是一个血淋淋的教训:攻击者没有修改源码,而是通过修改构建脚本注入了漏洞,成功绕过了源码审查。如果不是被意外发现,全世界大量的网络服务器都将门户大开。

为了回答这个终极问题,公司又投入了约一年的时间,最终证明了其构建流程的每一步都是安全的。故事的结局出人意料,但也在意料之中,尽管公司证明了一切,对方政府依然决定在未来几年内全面替换其产品。

从“信任”到“工程化信任”

看似两年的巨大投入付诸东流,但该公司高层却认为这是一次宝贵的学习机会,是一场深刻的公司内部革命。他们甚至想感谢当初提出这些问题的审查机构负责人,因为正是这些问题,帮助公司照见了自己工程体系中最深层次的挑战和不足。

为此,他们在前后5年里累计投入约20亿美元,专门用于提升软件工程能力。他们得出一个核心结论:在大规模、长周期的复杂系统中,“信任”早已不是一种感觉或承诺。信任,本身必须被设计和实现出来。信任,是一种工程能力。

而构建这种“工程化的信任”,其核心,就是本文真正要讨论的主题:依赖管理

重新定义问题:看见依赖的整座冰山

一提到依赖管理,我们脑海里浮现的通常是 package.json 里的 dependencies,或是 pom.xml 里的 dependency 标签。我们知道,这些依赖的变更会影响产品的功能和构建,它们的错误或缺失会导致功能异常或无法构建。

但这,就是依赖管理的全部了吗?难道只有这些变化才会影响软件制品及其构建过程吗?

让我们想一想,你用的哪个版本的编译器(这是 工具链依赖 )、一个细微差异的环境变量(这是 环境依赖 )、Makefile 里的编译选项(这是 配置依赖 ),甚至用于代码生成的模板文件(这是 数据依赖 )。这些看不见的“隐性”依赖,和代码变更一样,都能导致产品失败,或者引入致命的漏洞。它们的变化对产品功能和构建产生的影响,与那些软件包是一样的。既然随着研发的进行,这些要素也会变更、升级,那为什么不能也叫它们是依赖呢?

因此,我们必须将视角拉高,站在软件“生产”的全过程来看,并引入一个更为宏大的概念——“广义依赖”

“广义依赖”是指所有会影响软件生产过程和最终结果的要素。这不仅包括我们熟知的代码依赖(可称之为“狭义依赖”),更涵盖了以上提到的所有“隐性”依赖。

这个“广义依赖”网络,在真实世界里到底有多复杂?在一个真实的、拥有超过8400万行代码、由1500多名开发者维护超过10年的大型通信产品中,其“广义依赖”网络错综复杂,仅仅是其构建视图的15%就足以让人望而生畏。

之所以要从这样一个巨大规模的案例来看,是因为随着规模的增长,很多原本细小琐碎、经常被我们无视的问题,就会被指数级放大。大规模研发场景就像一个放大镜,帮助我们看清问题背后的本质。

当这个“广义依赖”网络变得如此庞大且大部分不可见时,我们日常工作中的那些痛点就来了。这些看似混乱的现象,可以归结为三大核心痛点:

  1. 变更扩散路径不清晰:底层一个库的小改动,到底会影响谁?影响范围有多大?没人能完全说清楚,精确评估影响范围成了一个不可能的任务。至于开发环境、构建环境的变更所带来的影响,则常常被忽视。一次看似平常的工具链升级,就可能让整条流水线停摆,故障不断。
  2. 集成滞后且脆弱:我们的CI/CD充满了不确定性,“本地能跑,CI挂了”或“CI出错了,本地无法复现”的场景屡见不鲜。随着规模增加,我们不得不在“快但不完整”和“完整但很慢”之间痛苦地选择。我们的集成过程,充满了大量的人工干预,以及……祈祷。
  3. 构建效率低下:一次完整的发布构建,需要7到8个小时。这意味着开发者提交代码后,要到第二天才能知道结果。为了提速,我们只好退而求其次,每次提交代码只做局部的集成和测试。但这样会把更大范围的集成问题,推迟到每个月发布版本的时候。每次整体集成时,团队都要花上一周甚至更久的时间来解决这些累积的集成问题。

必然的演进:从“黑盒遍历”到“显性工程化治理”

面对这样的困境,我们必须寻求演进。在小规模时,我们依赖构建工具的“黑盒遍历”模式。比如我们运行 make,它会自己扫描文件,计算和发现应该做什么。这很直接,也很有效。但随着规模增长,哪怕你一行代码都没改,仅仅是“全局扫描”这个动作本身,就成了巨大的浪费,是研发流程的最大瓶颈。

在一个有大约2000名开发者、使用 Bazel 构建的有数百万行代码的项目中,“黑盒遍历”的瓶颈也暴露无遗。这里有两个巨大的时间开销:首先,哪怕你一行代码都没改,仅仅是启动构建后,Bazel 对整个代码仓进行扫描分析,这个“全量扫描”阶段,就需要6到8分钟。除此之外,为了利用缓存,系统还需要下载约40G由大量小文件组成的中间制品,光是下载就需要30多分钟。大家可以想象一下,在CI流水线上,有上千个构建节点同时运行,并发地进行着全量扫描,又同时去下载这40G的缓存,这常常会引发网络风暴,直接拖垮网络,让所有人的构建时间都变得更长,而且极不稳定。

所以,新的模式是必然的选择,那就是“显性工程化治理”。简单来说就是“分而治之”。它的核心思想非常简单:我们不能再被动地依赖工具去“发现”了,我们必须主动地、显式地去定义和管理 我们的工程体系。我们要把过去隐性的东西,全部显性化。

这种“分而治之”,和我们过去为了提速而做的“局部集成”,有着本质区别。过去的局部集成,往往依赖开发者的直觉和手动选择,但这很容易造成影响范围的错误评估,也很容易遗漏掉隐藏的依赖,最终把风险和问题都推迟到最后的大集成阶段。而这里所说的“分而治之”,是基于精确、显性的广义依赖网络 ,进行系统性的、可预测的拆分。它的目标不是简单地“少构建一点”,而是精准地“只构建必要的部分”

这样带来的改变是极为明显的:系统能够自动、快速地计算出任何变更的精确影响范围,然后只针对这个范围进行完整的构建和测试,而其他所有未受影响的部分则可以直接复用缓存。并且这种复用是以构建单元为粒度进行打包和下载的,避免了过去下载海量小文件的低效方式,效率也更高。对于开发者来说,体验就从“为了快,我只测一小块,然后祈祷别出问题”变成了“我每次都运行完整的全局构建,但系统能让它在几分钟内就完成”。这就在速度和完整性之间找到了完美的平衡。

显性治理的三大支柱:拆解、封装、验证

如何实现“显性工程化治理”?其答案可以归结为三大核心实践支柱,它们环环相扣,共同构建起一个可靠的软件生产体系。

支柱一:拆解 (Chunking) —— 定义清晰的边界与输入

“拆解”是“分而治之”的前提,其核心是“小而有界” (Make it small and bounded)。它要求我们将庞大的系统,拆解成一系列具有清晰边界、可独立管理的构建单元。然而,这项工作之所以困难,其根源在于我们用来指导拆解的“地图”——那张广义依赖全景图,本身就是不可见和不完整的

要有效拆解,我们必须首先解决这张“地图”本身的三大缺陷,并采取相应的实践方法:

  1. 挑战:边界不清晰为了方便维护,我们常常会把源码各部分对环境的依赖合并后集中管理。这带来了好处,但恰恰是这种好处,模糊了每个构建单元的真实广义依赖边界。声明的依赖冗余了,就会在无关变更时重复构建,浪费资源;声明的依赖遗漏了,就会错误地命中缓存,造成难以排查的集成问题,最终只能靠“清空缓存再试一次”来解决。
  2. 挑战:存在“隐含依赖”CI服务器上的一个环境变量,基础镜像里的一个系统类库版本,这些真实地影响着构建结果的要素,却从未被正式地声明和管理,导致我们的依赖地图是不完整的。
  3. 挑战:连接被“切断”一个C++库怎么影响到一个Java服务?这种关系往往隐藏在某个CI脚本里,从任何一个语言的依赖工具看,这条连接都是不可见的。还有更糟糕的“幽灵依赖”,比如通过相对路径直接引用文件,这完全绕开了构建系统的感知。

为了绘制一幅精准的地图,我们的实践核心是将所有隐性要素显性化

  • 精准拆解构建上下文:我们必须明白,相同的源码并不能确保得到相同的制品。因此,我们拆的不仅仅是源码,更是它背后的完整构建上下文。每一个构建单元都必须拥有一个独立的、精准且完备的构建上下文定义,完整地捕获这个单元所有的广义依赖:工具链、库、配置等等。而且,拆解的过程也可能包括合并,比如把几个小单元合并成一个更大的构建单元。在这个过程中,就必须显式地解决它们各自广义依赖之间的冲突。
  • 发现并固化隐式依赖:让所有“不可见”都变得“可见”。这就要把CI环境、基础镜像、外部工具这些过去被认为是“理所应当”的生产要素,全部进行版本化和声明化管理。虽然我们已经实践了基础设施即代码(IaaC),但很多时候,它也仅仅是把配置用代码的形式保存在了代码仓里。如果我们真的认为这些是代码,那就应该更进一步:不仅要声明,还要像代码一样建立起与业务代码的依赖关系。一旦建立了这种关系,被依赖的要素,就应该被自动化地创建和提供出来。
  • 用声明式关系修复连接断裂:单元与单元之间的依赖关系,必须被显式地声明出来,避免通过命令、相对路径或者一段胶水脚本来连接。

支柱二:封装 (Fabrication) —— 打造可靠且确定性的生产过程

在通过“拆解”定义了清晰的输入单元后,“封装”的目标是“完整可交” (Make it whole and shippable)。它要求依据单元所声明的全部广义依赖,通过一个确定性的生产过程,像一个现代化的工厂一样,可靠地将其构建并组装成可交付的产品。其黄金标准是实现可复现构建 (Reproducible Build)

然而,这一过程面临两大核心挑战:输入的“非确定性”与过程的“不可靠性”

  1. 挑战:输入的非确定性当一套代码需要支持跨平台、多硬件以及多样的用户定制化需求时,有效的产品配置组合便会呈指数级增长,形成“产品配置组合爆炸”。每一个差异化的产品,都是源码、编译选项、功能开关、定制化配置等一系列输入的特定组合。这些组合中的细微差异,都可能通过复杂的构建逻辑判断引入未被预期的行为。更棘手的是,许多无法复现的问题,其根源还隐藏在那些未被版本化管理的“幽灵依赖”中,例如特定的运行时环境变量,或调试时手工指定的命令行参数。问题的复现之所以变得极为困难,正是因为我们难以精确还原导致问题发生的那一组特定的、非确定性的完整快照。
  2. 挑战:过程的不可靠性构建过程中访问了网络,或者依赖了当前的时间戳,这些都会引入未声明的输入,污染我们的广义依赖集。更常见的是为了应急的手工热修复,绕过了标准流程,引入了无法追踪的变更,比如在构建过程中动态修改文件、打补丁等。随着规模增加,构建和发布过程会演变成一个由大量任务组成的复杂依赖图,这又会把不稳定的影响放大,降低整体的可靠性。

为了应对这些挑战,打造一条可靠的生产线,必须遵循三大实践原则:

  • 管理配置组合爆炸 (Managing Configuration Explosion):要解决输入的非确定性,必须从“命令式”转向“声明式”,将不同的产品配置(如功能开关)本身也作为一种显性的依赖来管理。这能极大简化构建逻辑,让复杂的配置组合变得可追踪、可审计,而不是隐藏在脚本的 if-else 判断里。
  • 建立封闭的构建行为 (Hermetic Builds):为了确保过程的可靠,必须将构建过程隔离在“沙箱”中。这能确保输入源的纯净,杜绝一切未声明的外部干扰,如网络访问、随机文件读写、未定义的环境变量等。
  • 保障过程的幂等性 (Idempotency):这是可靠性的核心保证,即“相同的输入,永远得到相同的结果”。更进一步说,一个幂等的构建任务,在成功执行一次之后,立刻再次执行,结果也必须完全相同,不能对环境或产物造成任何累加的副作用。这不仅是为了消除时间戳、随机数等不确定性,更是为了防止构建过程本身污染工作区和环境。只有保证了幂等性,我们才能确保每一次构建都是一次“干净”的执行,从而放心地依赖缓存,也为最终实现可复现构建打下了坚实的基础。

支柱三:验证 (Verification) —— 建立完整且不可篡改的信任链

通过“拆解”我们看清了输入,通过“封装”我们规范了过程。现在,我们来到了最后,也是最关键的一环:“验证 (Verification)”。其目标是“可证可溯” (Prove it and trace it),即为我们每一个交付的制品,提供一份无可辩驳的“身份证明”,让它的全部广义依赖和整个生产过程,都变得透明、可追溯、可审计。

这项工作的核心挑战在于,传统模式下我们缺乏一条统一的、覆盖所有广义依赖的证据链。开篇故事中的三重质询,就完美地揭示了这条证据链是如何层层断裂的:

  1. “源码可信吗?” —— 这仅仅验证了广义依赖中很小的一部分,证据链在此刚刚开始。
  2. “产品是用这份源码构建的吗?” —— 这开始挑战从“源码”到“产品”的转换过程,这是证据链上的第一处关键断裂。
  3. “构建过程本身安全吗?” —— 这实际上是在拷问,构建过程所依赖的其它所有广义依赖,比如基础镜像、工具链,它们都可信吗?这是更深层次、也更隐蔽的断裂。

因此,“验证”的核心实践,就是要为每一个制品,建立一条从所有广义依赖源头,到最终产物,完整、统一、不可篡改的证据链。这条证据链必须是完整的,要超越源码版本,记录所有影响构建的广义依赖——工具链、脚本解释器、环境变量、运行时参数配置、构建中用到的系统类库等等。它也必须是准确的,最终产物的任何一个字节级的差异,都必须能在这条证据链里,找到对应的某个广义依赖输入的变化。

可复现构建 (Reproducible Build),正是对整条广义依赖证据链最强有力的物理保证。它将“信任”,从一句主观的“相信我”,变成了一项客观的、任何人都可以去验证的事实:“你可以自己去验证”。

结论:构建数据驱动的自验证闭环

“拆解(Chunking)”、“封装(Fabrication)”和“验证(Verification)”这三大支柱,并非孤立存在,而是共同构成了一个数据驱动的、自验证的工程闭环,这正是整套体系的核心精髓所在。

首先,它们形成了一条清晰的价值链:精确的“拆解”,为我们定义了清晰、有边界的单元及其广义依赖,这是所有信任的起点。这些清晰的输入,驱动了可靠的“封装”生产过程。可靠的过程,产出了带有完整广义依赖证据链的制品,从而可以用于“验证”。

更重要的是,“验证”的结果为这个闭环提供了强大的反馈机制。一方面,像一次不可复现的构建失败,会立刻暴露我们“拆解”时的不足,驱动我们去发现并固化新的隐式依赖,从而完善我们的依赖地图。另一方面,也是最关键的一点:既然一条完整的证据链能够解释产物的任何差异,那么它本身就构成了一份经过事实检验的、最权威的广义依赖清单。这意味着,我们可以从最终的证据链中反向推导出构建这个产物所需要的一切输入,从而用最终的结果,来验证我们最初对输入的定义是否精准、完备。

至此,一个自验证的闭环便形成了。我们不再是单向地“定义输入 -> 执行过程 -> 得到输出”,而是让输出的结果反过来证明我们对输入的定义是正确的。

回顾我们日常工作中遇到的构建效率、可靠性、漏洞追溯、架构治理等看似孤立、分散的问题,其实它们拥有一个共同的根源——广义依赖管理的缺失。通过“拆解-封装-验证”这套显性治理框架,我们可以在一个统一的视角下,系统性地解决这些问题。最终目标,是建立一个数据驱动的、可追溯、可自验证的,真正值得信赖的现代化软件生产体系。

(END)

我写了一个 Alfred Workflow,用于新建和加入 Zoom 会议。
现在支持免费和付费帐号,如果没有登录也可以直接进入会议。

使用方法:
1、直接在 Alfred 里面粘贴 Zoom 的会议链接,按下回车就可以自动加入会议。如果 Zoom 还没有运行,也会自动运行起来。
2、 直接在 Alfred 里面粘贴 Zoom 的会议链接后,按下 Command 键会 新建一个会议,而不是加入现有的会议。并且会自动把新建的会议的 url 放入剪贴板,我们只要直接粘贴就可以。
3、有 `zm` 关键字,直接按下回车就是新建一个会议;
4、在 `zm` 关键字后面可以继续添加 meeting id,按下回车会自动加入;
5、在 `zm` 关键字后面无论是否有 meeting id,按下 Command 键都会新建一个会议。

源码:https://github.com/chaifeng/alfred-zoomus

下载:https://github.com/chaifeng/alfred-zoomus/releases

This Alfred Workflow is used to start or join a Zoom meeting.

Support free or paid account, you can join a meeting without logged in

Usage:

  1. Paste a Zoom meeting URL in Alfred directly, press Enter, this will join an existing meeting.
  2. Paste a Zoom meeting URL in Alfred, press Command + Enter, it will start a new meeting. And put the URL of this new meeting into your clipboard automatically.
  3. A new keyword “zm” is used to start a new meeting.
  4. Append an existing Zoom meeting ID after the keyword “zm”, it will join this meeting.
    for examples: zm 123-456-789, zm 123456789
  5. Whether or not there is a meeting ID after keyword zm,press Command + Enter will always start a new meeting.

Source code: https://github.com/chaifeng/alfred-zoomus

Download: https://github.com/chaifeng/alfred-zoomus/releases

Dreamhost 的空间就要到期了,实在无法忍受在中国访问的龟速了。

昨天正式迁移到 Linode 东京机房,速度那叫一个爽,基本上带宽全占满。

前天是女性朋友的节日,大早上的写了一个段子,昨天竟然发现上了新浪微博的热门,Cooooool。

兜兜的照片也好久不放了,很纠结,担心万一被坏人利用把我家兜兜给骗走。

把基本情况先简述一下,首先是周六太原大雨,影响了到场率,不过还是有将近40人,而且还看到一些新面孔。其次是两位讲师的话题都很给力,演讲都超时,而且QA环节大家都很踊跃的提问。郭振的话题用了1个半小时才结束,张龙的话题更是达到了2个小时。最后5点50结束本次活动。

以下文绉绉的总结出自专业编辑李洋之手:

7月21日,QClub太原站如期在山西出版传媒集团一楼会议室举行,这已经是QClub第三次来到太原举行活动。今天的龙城太原下起雨来,这给炎炎夏季带来了丝丝凉意;而这场期盼已久的QClub技术社区活动对于技术相对滞后的太原地区而言,也是如同久旱逢甘霖一般及时与酣畅。此次活动中,增添了很多新的面孔,他们为QClub太原站社区活动添加了新的血液与活力。

大雨并没有浇灭前来参加活动人们的热情,在不小的会议室当中坐得是满满当当。技术开发人员也都希望在这场难得的“Android应用开发”的主题讲座中汲取自己所需要的养分并希望借此来解决在工作中所遇到的实际问题。好了,我们现在直入主题,看看讲师们今天会给我们分享那些先进理念与经验以及会带给我们哪些意想不到的惊喜!

首先是来自盛大创新院的高级研究员、乐众ROM项目组总架构师郭振分享的 Android 备份框架的架构与设计,以及如何将自己的服务集成到 Android 系统中。此次活动有一点明显地改善就是参与者的积极性与主动性较前两次有了显著地提高。在互动环节当中,参与者与讲师之间的交流更加自然,提问也是更加踊跃,奖品更是抢手……

接下来是联想集团全球应用开发部的高级工程师、InfoQ翻译团队编辑张龙分享的Android跨进程通信机制与AIDL,介绍了Activity与Service之间的通信原理。张龙这个名字,大家一定不陌生,这已经是第二次来到太原与大家分享经验,算是QClub太原站的老朋友了。大家对于张龙刚刚翻译出版的《Android Web应用高级编程》这本书产生了浓厚的兴趣,这本书涉及到一些跨平台移动开发技术的内容。这本书也是作为活动中的奖品来发放,在互动过程中大家的热情都很高涨,很想要获得这本精美IT图书,因此互动问答过程就格外积极与踊跃!

这已经是山西书海数字网络传媒科技有限责任公司第三次成功举办QClub太原站活动了,书海传媒对于推动太原地区IT技术交流,应该说是功不可没。最后,我们还是要再次感谢QClub太原站活动的本地赞助商——书海传媒的鼎力支持。

from 2002-06-14

 

08. May 2012 · 2 comments · Categories: Uncategorized · Tags:

先说这次活动的问题,一定要自我检讨,这次竟然忘记安排摄影师了,最后只有区区可数的几张照片。

再说高兴的事情,本次活动签到人数首次达到53人,这也是从去年7月开始第一次组织活动以来的最高记录。可能也和这次活动的主题《前端开发与用户体验》有关系吧,在太原的设计师多数都是美眉。从现场照片上也能看出,女生比以往的几次活动明显多了,然后……咳咳……人就来的多了。

然后说说讲师吧,分别是来自赞助商快乐妈妈(www.happymama.cn)的设计师赵敏,和来自阿里云的交互设计专家安勇。本地讲师与外地讲师相比,明显在演讲经验和内容的组织上比较欠缺,希望通过以后的活动可以提高本地讲师的演讲水平,也能够让太原的讲师去外地做分享。

这次活动准备的礼品还是比较多的,十本书和二十张2012QCon北京大会资料光盘。依然还是贯彻只要你参与或者与讲师互动,就能获得小礼物的宗旨。到活动的结束,就剩下了5张光盘。基本上都是在阿里云交互设计专家安勇的演讲结束后的提问环节,大家的热情都很高,一口气就发出了20份的礼物,让安勇都有点快招架不住了。

最后的 Open space 环节,与上次相比,稍显冷清了一点。可能因为设计师美眉们好多都提前离场的原因,然后……咳咳……人就走的更多了。

讲一个有趣的小插曲,本地赞助商快乐妈妈(www.happymama.cn)的技术负责人叫安磊,他在接到安勇后做自我介绍。结果安勇以为对方把他的名字记错了,还更正说“不是安磊,是安勇”。

总结,本次QClub太原站参与人数超出了预期,终于摆脱了参与人数倒数第一的帽子,以后一定要继续吸引IT美眉的参与。活动中大家的参与度还是比较高的,可能是大家都很关心这个主题,也可能是因为外地讲师是中韩混血帅哥。忘记安排摄影师了,重大失误。

最后一定要再次感谢赞助商快乐妈妈(www.happymama.cn),谢谢,这次的活动真的真的很成功。

2012 年第一次QClub太原站技术沙龙在山西出版传媒集团一楼会议室

举行,来自太原的数十名软件从业人员参与了此次活动。本次活动的主题是“Spring框架深度剖析”,主讲人有来自联想集团全球应用开发部高级工程师、InfoQ中文站翻译团队编辑张龙,也有太原本土资深软件开发专家和项目经理李永茂。李永茂讲师深入IoC核心实现,剖析了关键的部分源码;还分享了他在架构原理及设计思想方面的独到见解和一些关于Bean的解析问题。接下来,张龙讲师深入浅出地从源码角度揭示Spring 的架构设计与模式的应用;并分析了Spring对AOP的支持与实现方式及Java技术分析等内容。

讲师们的精彩演讲将本次活动频频推向高潮,所有参加活动的听众都认为不虚此行,觉得受益匪浅。在Open Space中,全场的气氛最为轻松活跃,听众们与讲师融为一体,相互交流经验,谈及一些实际工作中遇到的问题。全部活动结束时,天色已经很晚了,听众们仍然意犹未尽,继续讨论着下一次QClub的主题内容和如何将太原的社区活动搞得更加丰富一些。QClub太原站在交流声、掌声、欢笑声中画上了圆满的句号。

最后,再次感谢第一次QClub太原站活动的本地赞助商山西书海数字网络传媒科技有限责任公司的鼎力支持。

–分割线–

  • 上面的文字是赞助商专业编辑写的。
  • 最后 Open Space 环节,大家都忙着聊天忘记拍照片了。

 

Xcode 4 在 App Store 上售价 5 美元,作为一个优秀的开发环境,这个价格还是非常超值的,毕竟还有不少游戏的价格都不止 5 美元。
下载 Xcode 4 用了两天半,一直徘徊在30kB左右,昨晚凌晨2点起来看了一下,发现速度达到220KB,早上起来就下载完毕了。安装过程,果然和大部分人说的一样,停止在约98%的位置上。不过据我观察,其实Xcode已经安装成功了,如果在安装的过程中,打开一个终端,用命令
ps -A | fgrep Xcode
可以看到好几个进程,可能会有5、6个,其中一个类似下面所显示的:
923 ??         3:18.80 /usr/sbin/installer -verboseR -target / -pkg /Applications/Install Xcode.app/Contents/Resources/Xcode and iOS SDK.mpkg
这个就是大家提到的显示包内容,双击执行的那个文件,这个我们先不用管。
Screen shot 2011-03-17 at 上午09.57.50.png
然后继续等待安装过程,等一直到98%的位置上,长时间停止后,再次在终端里输入命令
ps -A | fgrep Xcode
会看到就剩下两个进程了,我的是:
911 ??         0:29.03 /Applications/Install Xcode.app/Contents/MacOS/Install Xcode -psn_0_385118
922 ??         0:00.08 /Applications/Install Xcode.app/Contents/Library/LaunchServices/com.apple.dt.InstallXcodeHelper launchd
Screen shot 2011-03-17 at 上午10.00.00.png
前面提到的id为 923 的进程已经结束,现在只要把 launchd 的这个进程杀掉就可以了,在我这里就是id为 922 的这个进程,用下面的命令:
sudo kill -9 922
执行完命令,我们会发现Xcode的安装窗口没了,现在用 Finder 打开 /Developer/Applications/ 双击 Xcode 就能正常用了。
我觉得这个问题可能和 launchd 有关系,曾经我在安装 MacFUSE 的时候也出现过一个奇怪的现象,安装就要结束的时候,CPU 飙升到 100%,长时间不动,把安装 MacFUSE 进程中有 launchd 的这个进程强制杀掉就可以了。
总之,Xcode 安装过程挺费时而且费事。用上面的方法没发现啥问题,刚才已经成功编译并测试了一个工程,很正常。如果觉得不放心,那就参照大家都用的方法:显示包内容,然后双击 Xcode and iOS SDK.mpkg。
看到还有人提到怎么备份安装文件,打开 Finder ,在 Applications 中找到 Install Xcode,拖动到移动硬盘或其他什么地方备份就行了。