当前位置: 首页 > 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…...

第14张 GROUP BY 分组

一、分组功能介绍 使用group by关键字通过某个字段进行分组&#xff0c;对分完组的数据分别 “SELECT 聚合函数”查询结果。 1.1 语法 SELECT column, group_function(column) FROM table [WHERE condition] [GROUP BY group_by_expression] [ORDER BY column]; 明确&#…...

笔记整理—linux驱动开发部分(10)input子系统与相关框架

关于输入类设备的系统有touch、按键、鼠标等&#xff0c;在系统中&#xff0c;命令行也是输入类系统。但是GUI的引入&#xff0c;不同输入类设备数量不断提升&#xff0c;带来麻烦&#xff0c;所以出现了struct input_event。 struct input_event {struct timeval time;//内核…...

[算法初阶]埃氏筛法与欧拉筛

素数的定义&#xff1a; 首先我们明白&#xff1a;素数的定义是只能整除1和本身&#xff08;1不是素数&#xff09;。 我们判断一个数n是不是素数时&#xff0c;可以采用试除法&#xff0c;即从i2开始&#xff0c;一直让n去%i&#xff0c;直到i*i<n c语言: #include<…...

【THM】linux取证 DisGruntled

目录 0x00 房间介绍 0x01 连接并简单排查 0x02 让我们看看做没做坏事 0x03 炸弹已埋下。但何时何地&#xff1f; 0x04 收尾 0x05 结论 0x00 房间介绍 嘿&#xff0c;孩子&#xff01;太好了&#xff0c;你来了&#xff01; 不知道您是否看过这则新闻&#xff0c;我…...

SpringBoot整合Freemarker(四)

escape, noescape 语法 <#escape identifier as expression>...<#noescape>...</#noescape>... </#escape> 用例 主要使用在相似的字符串变量输出&#xff0c;比如某一个模块的所有字符串输出都必须是html安全的&#xff0c;这个时候就可以使用&am…...

centos docker 安装 rabbitmq

安装docker 1.更新现有的软件包 首先&#xff0c;确保您的系统是最新的&#xff0c;可以通过运行以下命令来实现&#xff1a; sudo yum update -y 2.移除旧版本的Docker 如果您之前安装过Docker&#xff0c;可能需要先卸载旧版本。使用以下命令来卸载旧版本的Docker&#…...

手动实现promise的all,race,finally方法

Promise.all 是一个非常有用的工具&#xff0c;它接受一个 Promise 对象数组&#xff0c;并返回一个新的 Promise。当所有输入的 Promise 都成功解决时&#xff0c;新的 Promise 会解决为一个包含所有结果的数组&#xff1b;如果任何一个 Promise 被拒绝&#xff0c;新的 Prom…...

H5移动端预览PDF方法

新建页面 新建一个页面以便去预览对应的pdf 新建完后在 pages.json 文件内去新增对应路由 页面内容 <template><view class"page"><view class"pdf"><view id"demo"></view></view><view class"b…...

uniapp—android原生插件开发(1环境准备)

本篇文章从实战角度出发&#xff0c;将UniApp集成新大陆PDA设备RFID的全过程分为四部曲&#xff0c;涵盖环境搭建、插件开发、AAR打包、项目引入和功能调试。通过这份教程&#xff0c;轻松应对安卓原生插件开发与打包需求&#xff01; 项目背景&#xff1a; UniApp集成新大陆P…...

《潜行者2切尔诺贝利之心》游戏引擎介绍

潜行者2切尔诺贝利之心是基于虚幻5引擎&#xff0c;所以画面效果大家不必担心。游戏目前已经跳票了很久&#xff0c;预计发售时间是2024 年 11 月 21 日&#xff0c;这次应该不会再跳票。 潜行者2切尔诺贝利之心是虚幻5吗 答&#xff1a;是虚幻5。 潜行者官方推特之前回复了…...

用织梦系统做网站/关键词代发排名推广

String 1 &#xff1a;String s new String(“xyz”);创建了几个String Object? 二者之间有什么区别&#xff1f; 2个对象。”xyz”创建在字符串常量池中&#xff0c;new String()创建在堆中。 String,StringBuffer,StringBuilder区别 1、三者在执行速度上&#xff…...

互动科技 网站建设/百度官网下载安装免费

遗传算法地背包问题(c语言)基于遗传算法的0-1背包问题的求解摘要&#xff1a;一、前言组合优化问题的求解方法研究已经成为了当前众多科学关注的焦点&#xff0c;这不仅在于其内在的复杂性有着重要的理论价值&#xff0c;同时也在于它们能在现实生活中广泛的应用。比如资源分配…...

安康哪里做网站/seo优化技术教程

接【impala】在容器里编译impala(3)&#xff0c;也是最终篇了&#xff0c;反正我编译出来我需要的 impalad 了:) 主要是参考了 impala 的 wiki 文档&#xff0c;虽然文档略有点过时了&#xff0c;但大体上没什么问题&#xff0c;因为一开始编译的时候&#xff0c;我希望通过编…...

上海优化网站排名/桂平seo关键词优化

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; >>> 开源堡垒机 Jumpserver v1.4.8 发布了 , 该版本是 Bug 修复版本。 更新内容&#xff1a; 修复command filter 不记录操作日志的问题 LDAP支持无密码 录像上传设置中去掉了ceph&a…...

网站建设任务清单/产品推广软文范文

cin>>n>>m;//输入被除数和除数int lenn.length();int ans0;for(int i0;i<len;i){ans(int)(((long long)ans*10n[i]-0)%m);}cout<<ans; 转载于:https://www.cnblogs.com/Fy1999/p/8976511.html...

xunsearch做搜索网站/搜索引擎推广步骤

ByteArrayOutputStream 和 ByteArrayInputStream(理解) 特点: 都是在内存中对数据进行操作 , 不需要关闭 ByteArrayOutputStream * 构造方法: public ByteArrayOutputStream() ; * 成员方法: public byte[] toByteArray() ; public String toString() ; FileInputStream fis n…...