# 原则 {#principles}

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

## 默认采用约定 {#default-to-conventions}

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

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

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

## 清单文件是单一事实来源 {#manifests-are-the-source-of-truth}

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

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

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

## 将隐式行为显式化 {#make-the-implicit-explicit}

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

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

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

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

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


## 保持简洁 {#keep-it-simple}

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

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

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

## 从开发者体验出发 {#start-from-the-developers-experience}

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

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

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

## 错误难免会发生 {#errors-can-and-will-happen}

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

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

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

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

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