当前位置: 首页 > news >正文

Ente: 我们的 Monorepo 经验

在这里插入图片描述

原文:manav - 2024.10.29

九个月前,我们切换到了 monorepo。在此,我将介绍我们迄今为止的切换经验。

这并不是一份规范性的建议,而是一个经验的分享,目的是希望能够帮助其他团队做出明智的决策。

与大多数岔路不同,我们走过了两条路。因此,我会先描述导致我们改变的历史,概述我们在类似的情况中已经体验过的非 monorepo 方案,并因此能够更好地进行对比。

平台与 monorepo

Ente 的诞生可以追溯到五年前。它原本是一个端到端加密平台,用于存储 Vishnu 的所有个人数据,但后来发生了两件事: Vishnu 意识到需要这样一个平台的不仅仅是他自己,他还意识到要实现他的愿景需要做大量的工作。

于是,他从一个人变成了一个团队,并且不再试图处理所有的个人数据,而是将重点转移到其中的一个方面:Ente Photos,以此为起点让“飞船”起飞。对外界观察者来说,这似乎只是一个照片应用(实际上这确实是我们当前的具体目标),但背后驱动这一切的是我们对人类隐私权的坚持,即所有形式的个人数据都应受到保护。

我为什么要描述这些?因为从这个愿景来看,Ente 不是一个单一的应用程序,而是一个平台,将其代码存储在一个 monorepo 中是一个符合理念的选择。

这类似于 Linux 内核。大多数人不了解的是,按多项可量化指标衡量,全球最大的开源项目——Linux 内核本身——也是一个 monorepo。尽管它被称为“内核”,但实际上它是一整个平台,包括设备驱动程序等,代码组织为 monorepo 正是这种理念的体现。

坚持将 Ente 视为一个平台不仅仅是理念上的选择,它也带来了实际的益处。

例如,几年前,我们意识到还没有一款具有云备份功能的开源端到端加密 OTP 应用程序。于是我们为了自己的使用而构建了一款,因为是基于我们为照片应用创建的基础设施,实现并不难。

如今,这个副产品已成为世界上最受欢迎的具有上述特性的 OTP 应用。这看似是个意外,但其实不然,我们的计划一直是这样的:先建立一个稳固的平台,然后逐一处理我们需要的各种定制应用程序,以便更好地处理不同形式的数据。

微型代码仓库(Microrepos)

从理念上讲,Ente 作为一个 monorepo 是最合适的选择。但由于产品演变的历史因素,最开始并不是这样。硬件设备转变为软件,服务器组件在我们有能力进行审计之前是闭源的。像 Auth 这样的周末项目超出了它们的初衷,等等。

让我们倒带回两年前(仅仅为了选择一个大致对称的时间点)。虽然我们在包括开发人员数量在内的所有产品方面都在增长,但我们在增加工程人员方面非常谨慎,所以开发人员的数量并没有增加太多。因此,差不多是同样数量的开发人员在处理相同数量的产品(Ente Photos、Ente Auth),并同时支持多个平台(移动端、网页端、桌面端、服务器端、CLI)。

两年前,这些代码库分散在十几个仓库中。

到了今年二月,我们决定花时间完成服务器端开源的任务。这是一个很自然的时机来控制代码库的分散,于是我们借此机会切换到了 monorepo。

因此,作为一个规模相似的团队,做着类似的工作,我们已经体验了约一年分散的微型仓库设置,以及约一年集成的 monorepo 设置。

总结

如果要总结区别的话:切换到 monorepo 后,变化不大,而细微的变化都是正面的

我们对此并不感到意外。其中大多数人对代码仓库的组织方式并不十分在意,总体上对这种改变也没有太高的期望。大家的整体感觉是 monorepo 可能会更好,所以为什么不试试呢?既然没有人反对这个选择,我们就这么做了,但我们并没有试图通过这次改变“解决”什么问题。

事实上,整体上变化不大。我们依然对开发速度感到满意,所以它并没有拖慢我们。然而,确实有许多小的改进,所以接下来的部分我将深入探讨这些改进。

更少的重复劳动

这是最大的实际收益。我们需要做的重复劳动大大减少了。

举个例子,考虑以下的 pull request。它修改了用于计算设备上人脸嵌入的机器学习模型。

在这里插入图片描述

这个更改影响了(1)照片移动端应用,(2)照片桌面端应用,(3)照片网页端应用,以及(4)机器学习的基础代码。

在之前的分仓库模式下,这将是四个不同的 pull request,分别提交到四个不同的仓库中,并且需要通过评论将它们联系在一起以供日后参考。

现在,这是一份 pull request。容易审查,容易合并,容易回滚。

更少的子模块

子模块是一个让人恼火但确实有效的解决方案。问题是真实存在的,因此需要解决方案,而子模块确实是一个合适的解决方案,但它们仍然令人恼火。

这就是说,我们感谢 git 子模块的存在,它是解决实际代码组织问题的一种方法,但我们希望不需要使用它们。

Monorepo 减少了那些本应需要子模块的地方,因此这也是一个优势。

举个例子,之前 Ente Photos 的网页端和桌面端代码库之间是子模块关系。每次需要发布或推动重要的更改到主分支时,都会涉及到繁琐的 PR 操作。现在这些都不需要了。这两个相互依赖的代码现在可以在同一个提交中直接引用彼此,变更可以原子性地完成。

更多的 Stars

这是最大的营销收益。之前我们的 Stars 分散在十几个仓库中。如果每个仓库有一千个 Stars,我们总共有 12k Stars,但由于人的心理和 GitHub 推荐算法的工作方式,这远不如一个拥有 12k Stars 的单一仓库来的有影响力。

简单

我们在切换时的一个顾虑是,这可能会影响开发速度。我们以为会需要发明各种机制和约定来避免互相干扰。

但这些顾虑被证明是多余的。我们没有发明任何东西,只是静观其变,结果并不需要任何新方案。因此,对个人开发者来说,这次切换是轻松的,因为我们没有要求团队的任何成员改变他们的工作流程。

目前为止,也没有“仓库范围”的指导原则,除了两个:

  1. 不要有仓库范围的指导原则
  2. 不要动根目录文件夹

就是这样。每个文件夹内,或者每个子团队内部,可以自由选择任何组织方式、编码约定等等。

我意识到,这种轻松对我们来说,可能是由于团队规模较小,以及我们对彼此能力的高度信任。而这两个因素可能无法在其他团队中复制。

长期重构

跨仓库的重构需要比在同一个仓库内的重构更多的勇气。技术上来说,两者没有区别,但心理上的障碍却有所不同。

举个例子,我们已经将许多不同的网页应用合并到一个类似的设置中,而无需事先制定详细的计划。这一切都很自然地发生了,因为我们能够看到它们“彼此相邻”,代码复用的机会变得显而易见。

连接感

这种“在共享空间中工作但不在同一文件夹中工作”的方式,让我们比起以前单独或者以子团队的形式在各自的仓库提交代码时,感觉更紧密相连。

之前,很容易沉浸在各自的工作中(这是好事),但有时也会让人觉得自己只是在处理一个小部分,而无法看到整体(这不是好事)。

现在,大家仍然可以沉浸在自己的“文件夹”中,保留了这种沉浸感的好处。但也有额外的微妙提示让我们看到自己的工作是如何与整体相互关联的。因此,这是一种双赢的局面。

我所描述的可能有些抽象,所以让我举个例子。每当执行 git pull 时,会看到团队成员正在处理的所有变更。最近更改的文件名,文件中的更改数,最近的分支名,最近推送的标签。这些单独来看都是低信息量且不精确的信息载体,我甚至不会有意识地去看它们。

但随着时间的推移,我发现这些“环境提示”无意识地、自动地让我对周围发生的事情有了极好的感知。哪些功能正在开发,完成的阶段如何,哪些 bug 修复被推送了,最近发布了哪些版本。

类似的偶然信息交换也会发生在我打开 pull request 页面时,我会不经意间瞥见其他人正在处理的内容。

最棒的是,这一切都是颠覆性的、毫不费力的。每个人都在做自己的事,而仅仅因为大家都在这个共享的数字空间里工作,就自然地产生了一种意识和连接感。

总结

这篇文章已经很长了,远超我原本的预期,所以就此打住。

我本可以提供一些建议,但我认为没有什么特别的技术诀窍是必须的。在切换之前让我感到困扰的一个问题是我们将如何管理 GitHub 工作流,但事实证明这很简单,因为我们可以将 GitHub 工作流的范围限定为仅在特定文件夹的更改上运行。

从工程师的角度来看,回顾性文档如果没有“优缺点”部分是不完整的,但到目前为止,还没有发现任何对我们有影响的缺点,所以请原谅这部分内容的缺失。

从个人角度来说,我最喜欢 monorepo 的是,它让我感到自己像是“巨轮”的一部分,这艘巨轮正在无情地驶向完美,并且已经获得了不可阻挡的势头。我现在写的代码不再是一个孤立的 Web 组件,或一个 goroutine,或一个小的文档修复,而是这个单一平台的一部分——一个将超越我生命的平台。

相关文章:

Ente: 我们的 Monorepo 经验

原文:manav - 2024.10.29 九个月前,我们切换到了 monorepo。在此,我将介绍我们迄今为止的切换经验。 这并不是一份规范性的建议,而是一个经验的分享,目的是希望能够帮助其他团队做出明智的决策。 与大多数岔路不同&…...

Kafka java 配置

前言: 大家好,大家在springboot项目中,经常采用 KafkaListener 做为消费者。这个是spring为我们封装的。 但是某些情况 注解的方式并不能满足需求。这个时候就需要手动版本了。 介绍: 我们已经集成spring-Kafka 就不需要再…...

网络安全现状:复杂的威胁形势导致压力水平飙升

《2024 年网络安全状况》报告深入分析了当前网络安全挑战和趋势。 该报告重点介绍了几个关键的关注领域,包括人员短缺、技能差距、不断演变的威胁和预算限制,同时还指出了取得进展的领域,例如对威胁响应能力的信心增强以及对网络风险评估的认…...

【机器学习】强化学习(1)——强化学习原理浅析(区分强化学习、监督学习和启发式算法)

文章目录 强化学习介绍强化学习和监督学习比较监督学习强化学习 强化学习的数学和过程表达动作空间序列决策策略(policy)价值函数(value function)模型(model) 强化学习和启发式算法比较强化学习步骤代码走…...

【SoC设计指南 基于Arm Cortex-M】学习笔记1——AMBA

AMBA简介 先进微控制器总线架构(Advanced Microcontroller Bus Architecture,AMBA)是用在arm处理器上的片上总线协议规范集。 AMBA总线协议规范集包含AHB、APB、AXI等。 AHB:先进高性能总线(Advanced High-performance Bus) APB&…...

flutter鸿蒙模拟器 Win环境调试报错问题记录(暂未解决)

前情提要: 1、flutter项目已经正确生成了ohos项目 2、flutter和鸿蒙的环境变量配置正确 3、ohos项目执行flutter build hap成功 4、没有真机,使用win环境创建的x86模拟器 问题状态 使用模拟器运行ohos,控制台提示“安装HAP 报 code:9568347错…...

详解Rust标准库:HashSet

## 查看本地官方文档安装rust后运行 rustup doc查看The Standard Library即可获取标准库内容 std::collections::hash_set::HashSet定义 HashSet是一种集合数据结构,它只存储唯一的元素。它主要用于检查元素是否存在于集合中,或者对元素进行去重操作&…...

记录学习react的一些内容

由于是在公司实际项目中学习&#xff0c;所以不是很完整 需要一点一点的学 1.React.useState 类似于vue中的ref 可以修改状态 但是是异步的 感觉不好用 const [wishData, setWishData] React.useState<any>(null); 只能使用setxxx来修改 2.useEffect(()>{},[]) 类…...

json绘制热力图

首先需要一段热力信息的json&#xff0c;我放在头部了。 然后就是需要de-geo库了。 实现代码如下&#xff1a; import * as d3geo from d3-geoimport trafficJSON from ../assets/json/traffic.jsonlet geoFun;// 地理投影函数// let info {max: Number.MIN_SAFE_INTEGER,mi…...

linux 下查看程序启动的目录

以azkaban为例 第一步、ps -ef | grep azkaban 查询出进程号 第二步、cd /proc/ 第三步 、cd 进程号 第四部 ll 查看详情 查看jar 位置 查看jar 启动命令...

书生浦语第四期基础岛L1G2000-玩转书生「多模态对话」与「AI搜索」产品

文章目录 一、MindSearch二、书生浦语三、书生万象四、进阶任务 一、MindSearch MindSearch 是一个开源的 AI 搜索引擎。它会对你提出的问题进行分析并拆解为数个子问题&#xff0c;在互联网上搜索、总结得到各个子问题的答案&#xff0c;最后通过模型总结得到最终答案。书生浦…...

保护Kubernetes免受威胁:容器安全的有效实践

安全并非“放之四海而皆准”的解决方案&#xff0c;相反地&#xff0c;它更多的是一个范围&#xff0c;受其应用的特定上下文的影响。安全领域的专业人士很少宣称什么产品是完全安全的&#xff0c;但总有方法可以实现更强的安全性。在本文中&#xff0c;我们将介绍各种方法来支…...

【客观理性深入讨论国产中间件及数据库-科创基础软件】

随着国产化的进程&#xff0c;越来越多的国企央企开始要求软件产品匹配过程化的要求&#xff0c; 最近有一家银行保险的科技公司对行为验证码产品就要求匹配国产中间件&#xff0c; 于是开始了解国产中间件都有哪些厂家 一&#xff1a;国产中间件主要产品及厂商 1 东方通&…...

MFC中Excel的导入以及使用步骤

参考地址 在需要对EXCEL表进行操作的类中添加以下头文件&#xff1a;若出现大量错误将其放入stdafx.h中 #include "resource.h" // 主符号 #include "CWorkbook.h" //单个工作簿 #include "CRange.h" //区域类&#xff0c;对Excel大…...

AWS S3在客户端应用不能使用aws-sdk场景下的文件上传与下载

简介 通常情况下&#xff0c;应用程序上传文件到AWS S3&#xff0c;会使用aws-sdk&#xff0c;但是有些情况下&#xff0c;客户端应用会有安装限制&#xff0c;比如不能安装aws-sdk&#xff0c;此时我们就需要通过其他方式实现文件上传与下载。 这里我们提供一个服务端&#…...

深入解析 Transformers 框架(四):Qwen2.5/GPT 分词流程与 BPE 分词算法技术细节详解

前面我们已经通过三篇文章&#xff0c;详细介绍了 Qwen2.5 大语言模型在 Transformers 框架中的技术细节&#xff0c;包括包和对象加载、模型初始化和分词器技术细节&#xff1a; 深入解析 Transformers 框架&#xff08;一&#xff09;&#xff1a;包和对象加载中的设计巧思与…...

【Python-AI篇】K近邻算法(KNN)

0. 前置----机器学习流程 获取数据集数据基本处理特征工程机器学习模型评估在线服务 1. KNN算法概念 如果一个样本在特征空间中的K个最相似&#xff08;即特征空间中最邻近&#xff09;的样本中大多数属于某一个类别&#xff0c;则该样本也属于这一个类别 1.1 KNN算法流程总…...

aws xray如何实现应用log和trace的关联关系

参考资料 https://community.aws/tutorials/solving-problems-you-cant-see-using-aws-x-ray-and-cloudwatch-for-user-level-observability-in-your-serverless-microservices-applicationshttps://stackoverflow.com/questions/76000811/search-cloudwatch-logs-for-aws-xra…...

centos服务器登录失败次数设定

实现的效果 一台centos服务&#xff0c;如果被别人暴力或者登录次数超过多少次&#xff0c;就拒绝或者在规定时间内拒绝ip登录。这里使用的是fail2ban 安装fail2ban sudo yum install epel-release -y # 先安装 EPEL 源 sudo yum install fail2ban -y配置fail2ban # 复制默…...

实时高效,全面测评快递100API的物流查询功能

一、引言 你是否曾经在网购后焦急地等待包裹&#xff0c;频繁地手动刷新订单页面以获取最新的物流信息&#xff1f;或者作为一名开发者&#xff0c;正在为如何在自己的应用程序中高效地实现物流查询功能而发愁&#xff1f;其实&#xff0c;有一个非常好用的解决方案——快递10…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...