如何在一个 Web 应用程序中对不同功能进行版本控制?
How to version-control different features within one web application?
我们有一些网络应用程序,现在这些网站正在升级,这不是第一次,但是对于用户和开发人员来说,控制版本变得非常困难。
我们有很多客户,其中一些是运行相同的应用程序,但他们需要付费升级。但是,并非所有客户都为升级付费,因此我们有一些客户 运行 一个版本和另一个客户 运行 另一个版本。
我们有两种方式,正在研究第三种方式:
- 把版本放在路径里,像这样:
www\project\version\system-files
但是这种方式让一些用户感到困惑,因为对他们来说URL变成了:www.website.com/app-version
,当系统升级时,URL变了。
- 把版本放在函数里,像这样:
function V1_functionX()
当函数需要升级时,我们新建一个函数,名称为V2_functionX
。但是,这样创建了一个"fat"的网站,团队在开发的过程中犯了一些错误,因为我们没有"one development version",而是"many versions to develop",而且有些功能在多个网站中使用.
第一种方式很久以前就被废弃了。我们开发了 web 应用程序,"closed the version",所有请求都包含在升级版本中,完成后的版本也是 "closed"。但这太慢了,也进行了更正和部署 "small upgrades"
我们在另外一家公司讲过方法:他们"shutdown"给网站升级了系统。这可能是我们的方式。
但是,如果有人有其他关于不关闭网站以升级应用程序的想法,我们将很乐意倾听。
注意:这与 SVN 无关。
我们工作中有这个:
- 本地开发网站(SVN)
- 所有开发者测试的开发服务器
- 一切正常的预生产
- Prod(来自 preprod 的 rsync)
2 个服务器之间的 rsync 非常快,当我们进行重大更新时不到 5 秒
您说您有不同版本的应用程序,必须为不同的客户维护。我希望您不需要我告诉您这会显着增加整个系统的复杂性,因此您的首要任务是减少并行维护的版本数量。
API服务有同样的问题:提供了更多功能的新版本,但需要维护旧版本,让新版本有时间稳定,给用户足够的时间升级代码。你的困难是相似的。因此,我要问的第一个问题是是否可以只维护两个版本。
如果不可能,请至少尝试尽量减少并发版本的数量:在必须创建新版本的情况下,您需要鼓励用户从一个版本迁移到另一个版本。 (你说过你不能将你的用户统一到一个版本上,但是如果没有关于你的确切用例的更多信息,就不可能提供一个独立的观点)。因此,也许一种方法是永远不要维护超过五个版本。
您可以采取多种策略来降低现有系统的复杂性。首先,考虑将您的代码分成 "core" 个所有版本都必须具备的功能。这对所有版本都是通用的,因此如果您在此处修复错误,所有客户都将从修复中受益。这可能是可见功能(例如产品编辑屏幕)或框架功能(例如结帐时强制使用 SSL)。
您的核心库和特定于客户端的函数可以驻留在一组库中,如下所示:
/project/core
/project/versions/1/Class.php
/project/versions/1.1/Class.php
/project/versions/2/Class.php
/project/versions/2.1.1/Class.php
/project/versions/...
(Class.php
当然是一个例子 - 实际上这里会有很多 class 文件,每个文件都适当命名。)
这样,您就不需要调用带有 V1_
前缀的函数,因为这将需要在很多地方复制您的版本选择代码。最好只加载与正确版本相关的库,只要函数名称在所有版本中都相同,您就可以只使用函数名称,库加载程序会处理其余部分。
另一种方法是使用插件,就像 WordPress 一样。在添加插件的地方,它通过添加新的或不同的行为来修改一些核心功能。 "middleware" 设计模式在这里可能很有用 - Slim 框架(毫无疑问还有其他框架)使用这种方法将预调用或 post 调用挂钩添加到现有路由处理程序,从而提供了一种干净的机制来以各种组合编辑现有功能。
综上所述,您目前的情况不仅是管理问题,而且会花费您缓慢的开发时间和额外的调试时间。虽然上述方法对于降低某些复杂性仍然是必要的,但也请考虑:
- 强制落后的客户升级到您当前支持的版本之一
- 免费将落后的客户端升级到受支持的最旧版本
基于新信息的一些额外想法。我考虑过将代码拆分到单独的存储库中是否有帮助,每个客户一个。但是我想知道是否不能保证他们会;即使您在使用 Composer 或 Git 子模块时提取了核心功能,您的最新核心代码和最早的客户端代码之间仍然存在差异的可能性。在某些时候,您最落后的客户会阻碍核心开发。
您始终可以将此客户端留在废弃的版本上,但如果他们发现了错误,则不值得从您的最新核心向后移植修复程序,因为这会给您带来您一直以来的所有兼容性问题试图避免。他们要么升级到适用于最新核心的最低客户端版本(并在必要时付费),要么无限期地容忍错误。
您提到过每个客户都有自己的数据库。这在某种程度上是有帮助的,因为这意味着客户端版本并不完全受核心强制的数据库模式决策的约束。但是,这仍然会对您可以移动到核心的代码量产生连锁反应。
例如,假设您有七个客户,其中六个有一个包含电子邮件地址的用户实体,用于处理密码更改请求(一个客户有一个没有此字段的用户实体)。这意味着,如果笨拙的模式可能不会改变,核心就不能假定电子邮件地址可用。 (在这种微不足道的情况下,免费升级 odd-one-out 可能更便宜,这样更多的代码可以进入核心,而不是维护像版本增强这样的标准东西)。
考虑到复杂程度,而且听起来您要长期维护它,我认为您应该设置一些单元和功能测试。您还需要将它们拆分为 "core" 和 "per version"。如果你发现一个错误,不管它是否是由功能版本控制引起的,写一个失败的测试,然后修复它。然后,您将拥有 - 至少在理论上 - 一种检查更改是否会以您未曾设想的方式影响特定客户端版本的方法。
我们有一些网络应用程序,现在这些网站正在升级,这不是第一次,但是对于用户和开发人员来说,控制版本变得非常困难。
我们有很多客户,其中一些是运行相同的应用程序,但他们需要付费升级。但是,并非所有客户都为升级付费,因此我们有一些客户 运行 一个版本和另一个客户 运行 另一个版本。
我们有两种方式,正在研究第三种方式:
- 把版本放在路径里,像这样:
www\project\version\system-files
但是这种方式让一些用户感到困惑,因为对他们来说URL变成了:www.website.com/app-version
,当系统升级时,URL变了。
- 把版本放在函数里,像这样:
function V1_functionX()
当函数需要升级时,我们新建一个函数,名称为V2_functionX
。但是,这样创建了一个"fat"的网站,团队在开发的过程中犯了一些错误,因为我们没有"one development version",而是"many versions to develop",而且有些功能在多个网站中使用.
第一种方式很久以前就被废弃了。我们开发了 web 应用程序,"closed the version",所有请求都包含在升级版本中,完成后的版本也是 "closed"。但这太慢了,也进行了更正和部署 "small upgrades"
我们在另外一家公司讲过方法:他们"shutdown"给网站升级了系统。这可能是我们的方式。
但是,如果有人有其他关于不关闭网站以升级应用程序的想法,我们将很乐意倾听。
注意:这与 SVN 无关。
我们工作中有这个:
- 本地开发网站(SVN)
- 所有开发者测试的开发服务器
- 一切正常的预生产
- Prod(来自 preprod 的 rsync)
2 个服务器之间的 rsync 非常快,当我们进行重大更新时不到 5 秒
您说您有不同版本的应用程序,必须为不同的客户维护。我希望您不需要我告诉您这会显着增加整个系统的复杂性,因此您的首要任务是减少并行维护的版本数量。
API服务有同样的问题:提供了更多功能的新版本,但需要维护旧版本,让新版本有时间稳定,给用户足够的时间升级代码。你的困难是相似的。因此,我要问的第一个问题是是否可以只维护两个版本。
如果不可能,请至少尝试尽量减少并发版本的数量:在必须创建新版本的情况下,您需要鼓励用户从一个版本迁移到另一个版本。 (你说过你不能将你的用户统一到一个版本上,但是如果没有关于你的确切用例的更多信息,就不可能提供一个独立的观点)。因此,也许一种方法是永远不要维护超过五个版本。
您可以采取多种策略来降低现有系统的复杂性。首先,考虑将您的代码分成 "core" 个所有版本都必须具备的功能。这对所有版本都是通用的,因此如果您在此处修复错误,所有客户都将从修复中受益。这可能是可见功能(例如产品编辑屏幕)或框架功能(例如结帐时强制使用 SSL)。
您的核心库和特定于客户端的函数可以驻留在一组库中,如下所示:
/project/core
/project/versions/1/Class.php
/project/versions/1.1/Class.php
/project/versions/2/Class.php
/project/versions/2.1.1/Class.php
/project/versions/...
(Class.php
当然是一个例子 - 实际上这里会有很多 class 文件,每个文件都适当命名。)
这样,您就不需要调用带有 V1_
前缀的函数,因为这将需要在很多地方复制您的版本选择代码。最好只加载与正确版本相关的库,只要函数名称在所有版本中都相同,您就可以只使用函数名称,库加载程序会处理其余部分。
另一种方法是使用插件,就像 WordPress 一样。在添加插件的地方,它通过添加新的或不同的行为来修改一些核心功能。 "middleware" 设计模式在这里可能很有用 - Slim 框架(毫无疑问还有其他框架)使用这种方法将预调用或 post 调用挂钩添加到现有路由处理程序,从而提供了一种干净的机制来以各种组合编辑现有功能。
综上所述,您目前的情况不仅是管理问题,而且会花费您缓慢的开发时间和额外的调试时间。虽然上述方法对于降低某些复杂性仍然是必要的,但也请考虑:
- 强制落后的客户升级到您当前支持的版本之一
- 免费将落后的客户端升级到受支持的最旧版本
基于新信息的一些额外想法。我考虑过将代码拆分到单独的存储库中是否有帮助,每个客户一个。但是我想知道是否不能保证他们会;即使您在使用 Composer 或 Git 子模块时提取了核心功能,您的最新核心代码和最早的客户端代码之间仍然存在差异的可能性。在某些时候,您最落后的客户会阻碍核心开发。
您始终可以将此客户端留在废弃的版本上,但如果他们发现了错误,则不值得从您的最新核心向后移植修复程序,因为这会给您带来您一直以来的所有兼容性问题试图避免。他们要么升级到适用于最新核心的最低客户端版本(并在必要时付费),要么无限期地容忍错误。
您提到过每个客户都有自己的数据库。这在某种程度上是有帮助的,因为这意味着客户端版本并不完全受核心强制的数据库模式决策的约束。但是,这仍然会对您可以移动到核心的代码量产生连锁反应。
例如,假设您有七个客户,其中六个有一个包含电子邮件地址的用户实体,用于处理密码更改请求(一个客户有一个没有此字段的用户实体)。这意味着,如果笨拙的模式可能不会改变,核心就不能假定电子邮件地址可用。 (在这种微不足道的情况下,免费升级 odd-one-out 可能更便宜,这样更多的代码可以进入核心,而不是维护像版本增强这样的标准东西)。
考虑到复杂程度,而且听起来您要长期维护它,我认为您应该设置一些单元和功能测试。您还需要将它们拆分为 "core" 和 "per version"。如果你发现一个错误,不管它是否是由功能版本控制引起的,写一个失败的测试,然后修复它。然后,您将拥有 - 至少在理论上 - 一种检查更改是否会以您未曾设想的方式影响特定客户端版本的方法。