跳到内容

原则

本页介绍构成 Tuist 设计与开发基石的原则。这些原则随项目演进,旨在确保与项目基础保持一致的可持续增长。

默认采用约定#

Tuist 存在的原因之一是 Xcode 在约定方面较弱,导致项目复杂、难以扩展和维护。因此,Tuist 采取不同的方法,默认采用简洁且经过精心设计的规定。开发者可以选择退出这些约定,但这是一个需要刻意做出的决定,并不自然。

例如,有一种约定是通过提供的公共接口来定义目标之间的依赖关系。通过这种方式,Tuist 确保生成的项目具备正确的配置,使链接能够正常工作。开发者也可以选择通过构建设置来定义依赖关系,但这样做是隐式的,因此会破坏依赖于遵循某些约定的 Tuist 功能,如 tuist graph 或 tuist cache。

我们默认采用约定的原因是,我们能替开发者做出的决策越多,他们就越能专注于为应用打造功能。当像许多项目那样缺乏约定时,我们不得不做出与其他决策不一致的决定,从而导致难以管理的意外复杂性。

清单文件是单一事实来源#

多层配置及其之间的契约会导致项目设置难以理解和维护。试想一个普通项目:项目定义位于 .xcodeproj 目录中,CLI 逻辑位于脚本中(例如 Fastfiles),CI 逻辑位于流水线中。这是我们需要维护的三个层级及其之间的契约。你是否经常遇到这样的情况:修改了项目中的某些内容,一周后才发现发布脚本出了问题?

我们可以通过使用单一事实来源——清单文件来简化这一点。这些文件为 Tuist 提供生成 Xcode 项目所需的信息,开发者可以使用这些项目来编辑文件。此外,它还允许使用标准命令从本地或 CI 环境构建项目。

Tuist 应封装复杂性,并提供简洁、安全且令人愉悦的接口,以尽可能显式地描述项目。

将隐式行为显式化#

Xcode 支持隐式配置。一个很好的例子是推断隐式定义的依赖关系。虽然隐式性在配置简单的小项目中可以接受,但随着项目规模增大,它可能导致速度变慢或出现异常行为。

Tuist 应为隐式的 Xcode 行为提供显式 API。它还应支持定义 Xcode 的隐式行为,但以鼓励开发者选择显式方式的方式实现。支持 Xcode 的隐式行为和复杂细节有助于 Tuist 的采用,之后团队可以花时间消除这些隐式性。

依赖关系的定义就是一个很好的例子。虽然开发者可以通过构建设置和阶段来定义依赖关系,但 Tuist 提供了一个优雅的 API,鼓励大家采用。

将 API 设计为显式,使 Tuist 能够对项目执行一些否则无法实现的检查和优化。 此外,它还启用了诸如 tuist graph(导出依赖关系图的表示)或 tuist cache(将所有目标缓存为二进制文件)等功能。

Tip

我们应将每次从 Xcode 移植功能的请求视为通过简洁且显式的 API 简化概念的机会。

保持简洁#

扩展 Xcode 项目时的主要挑战之一是Xcode 向用户暴露了大量复杂性。 因此,团队的“巴士系数”很高,只有少数人理解项目以及构建系统抛出的错误。这是一种糟糕的状况,因为团队依赖于少数几个人。

Xcode 是一款出色的工具,但多年的改进、新平台和编程语言都反映在其表面上,使其难以保持简洁。

Tuist 应抓住机会保持简洁,因为处理简单的事物既有趣又能激励我们。没有人愿意花费时间调试编译过程末尾发生的错误,或弄清楚为什么无法在设备上运行应用。Xcode 将任务委托给其底层构建系统,在某些情况下,它在将错误转化为可操作项方面做得非常糟糕。你是否曾遇到 “未找到框架 X” 错误却不知如何应对?想象一下,如果我们能得到一份潜在根本原因列表该多好。

从开发者体验出发#

Xcode 周围缺乏创新(或者说,不如其他编程环境那样多)的部分原因是,我们往往从现有解决方案开始分析问题。 因此,如今我们发现的大多数解决方案都围绕相同的想法和工作流程。虽然将现有解决方案纳入考量是好的,但我们不应让它们限制我们的创造力。

我们喜欢像Tom Preston在这期播客中所说的那样思考:“大多数事情都可以实现,只要你脑海中有想法,在宇宙约束允许的范围内,你很可能通过代码将其实现。” 如果我们设想理想的开发者体验,实现它只是时间问题——从开发者体验出发分析问题,为我们提供了独特的视角,从而引导我们打造出用户喜爱的解决方案。

我们可能会忍不住随大流,即使这意味着继续忍受众人抱怨的不便。不要这样做。我期望如何归档应用?我希望代码签名是怎样的?我可以用 Tuist 帮助简化哪些流程?例如,添加对Fastlane的支持是解决某个问题的方案,但我们需要先理解这个问题。通过追问“为什么”,我们可以找到问题的根源。一旦明确动机来源,我们就可以思考 Tuist 如何提供最佳帮助。也许解决方案是与 Fastlane 集成,但在做出权衡之前,重要的是不要忽视其他同样可行的方案。

错误难免会发生#

作为开发者,我们天生倾向于忽视错误可能发生的事实。因此,我们在设计和测试软件时往往只考虑理想场景。

Swift、其类型系统以及架构良好的代码有助于防止某些错误,但无法防止所有错误,因为有些错误超出我们的控制范围。我们不能假设用户始终拥有网络连接,也不能假设系统命令总会成功返回。Tuist 运行的环境并非我们可控的沙盒,因此我们需要努力理解它们可能如何变化并影响 Tuist。

处理不当的错误会导致糟糕的用户体验,用户可能会因此失去对项目的信任。我们希望用户享受 Tuist 的每一个细节,甚至包括我们呈现错误的方式。

我们应该站在用户的角度,设想我们期望错误提示告诉我们什么。如果编程语言是错误传播的通道,而用户是错误的接收者,那么错误信息应该使用目标用户(即用户)所使用的语言编写。它们应包含足够的信息以说明发生了什么,并隐藏不相关的信息。此外,它们应具备可操作性,告诉用户可以采取哪些步骤来恢复。

最后但同样重要的是,我们的测试用例应考虑失败场景。这不仅确保我们按预期处理错误,还能防止未来的开发者破坏这一逻辑。