Ask AI: How about converting data manipulation to unstorage adaptation?
背景#
我是一名程序员。萌生一个想法,github 上有 unjs/unstorage 这个库,通过接入不同的 driver 适配层,把操作数据封装为 hasItem getItem setItem remoteItem。映射前端 localStorage 的概念。
我在想,用这种方式去适配前端操作数据的场景,还挺好的,技术不难还很容易出成果,简化学习的成本。比如,我有一个 wordpress api,我可以不学习具体用法,如果你需要更新网站信息,就使用我提供的 unstorage-wp-site,如果想回复评论就 unstorage-wp-comments.setItem。类似的,使用 get/post/put/delete 这种封装思路,就是复用已有的概念,你觉得怎么样?
我和 AI 的对话#
Claude3Opus 这样回复
这是一个很棒的想法!将不同的数据操作抽象为类似 localStorage 的通用接口,可以大大简化开发者的学习和使用成本。我对你的思路有以下一些想法和建议,供参考:
- 接口设计要尽可能简单易懂,贴近开发者已有的知识概念。你提到的 get/set/remove/has 等方法就很好,基本上看名字就知道作用。
- 考虑好异步操作。很多存储调用都是异步的,需要返回 Promise。可以参考 IndexedDB 的异步接口设计。
- 错误处理很关键。接口调用要能优雅地处理和传递错误,提供清晰的错误信息和代码。
- 版本管理和数据迁移。如果存储 schema 有变化,要考虑新旧版本兼容和数据迁移。
- 灵活可配置。提供良好的可配置性,让使用者可以自定义行为,如自定义 key 前缀、数据有效期、缓存策略等。
- 优秀的文档。编写清晰、全面的文档和使用示例,可以降低开发者的学习门槛。
- 单元测试。对各个存储适配层编写完善的单元测试,确保稳定性。
- TypeScript 支持。如果项目用 TS 编写,可以考虑提供完整的类型声明文件。
有人反对,他说好像更麻烦了,unstorage-wp-xxx.setItem, 这个 xxx 就是学习成本。只是将成本从一个地方转移到另一个地方。还多了一层,系统更复杂了[旺柴]
Caude3Opus 这样回复
这个反对意见提出了很好的观点。引入 unstorage 这个中间层,从某种程度上讲,的确是把学习成本从底层 API 转移到了 unstorage 的适配层。开发者需要去了解 unstorage 的概念和使用方法,以及每个适配器的具体用途。这无疑增加了一定的学习负担和系统复杂度。
但我认为,从长远来看,unstorage 这种方案还是有价值的,主要有以下几点考虑:
- 统一的接口。不同的存储方案有各自的接口和用法,通过 unstorage 提供统一的接口,可以降低开发者在不同存储方案之间切换的学习成本。
- 封装复杂性。有些存储方案的接口比较复杂,参数很多。unstorage 可以封装这些复杂性,提供更简单易用的接口。
- 可以沉淀出业务层面的通用模块。比如 unstorage-wp-site、unstorage-wp-comments 这种业务层面的适配模块,可以沉淀为项目的基础设施,在多个功能模块中复用。
- 利于统一错误处理、监控等基础设施。有了统一的接口,可以在 unstorage 层面统一进行错误处理、日志监控等。
- 更好的可测试性。通过 unstorage 适配层,可以方便地 mock 存储接口进行单元测试。
我认为如果有多个 driver 适配层,就可以进一步形成生态和插件市场。但这会带来新的问题,不同的 sdk 思路不同,可能产生 api 不一致的情况。请你继续作为反对意见方,对我进行挑战,提出你的各种反对意见。
好的,我来继续扮演反对意见方,对你的想法提出质疑和挑战:
- API 不一致的问题。正如你提到的,不同的 sdk 思路不同,可能导致 API 设计风格迥异。比如有的 sdk 喜欢用 get/set/remove, 有的喜欢用 read/write/delete, 参数名和类型也可能不一致。这样会导致开发者在使用不同的 unstorage 适配层时,需要频繁查阅文档,增加了开发成本。
- 质量参差不齐。开放的生态和插件市场,意味着任何人都可以贡献 unstorage 适配层。但不同贡献者的代码质量、文档完善程度、维护响应速度等都是未知数。这可能导致开发者在选择适配层时难以评估,集成后遇到问题难以得到支持。
- 版本兼容问题。如果 unstorage 的核心接口发生了变化,可能导致某些适配层无法兼容新版本。开发者可能需要等待适配层作者的更新,或者自己动手修改适配层代码。这种依赖关系可能会影响项目进度。
- 过度抽象的风险。有些存储方案可能本身已经提供了很好的接口,再通过 unstorage 封装一层,可能是一种过度抽象,反而增加了复杂度和性能开销。
- 学习成本的问题。虽然 unstorage 提供了统一的接口,但开发者仍然需要了解每个适配层对应的具体存储方案。比如 unstorage-redis 的使用场景和 unstorage-fs 是不同的。开发者需要了解这些差异,才能做出正确的选择。这种学习成本不可避免。
- 生态的割裂。不同的项目和团队,可能会选择不同的 unstorage 适配层。这可能导致社区生态的割裂,不同项目之间的代码难以复用。反而不如直接使用原生的存储方案,便于不同项目之间的协作。
- 性能问题。unstorage 作为一个中间层,可能会引入一些性能开销。特别是在高并发、大数据量的场景下,这种开销可能会成为系统的瓶颈。
- 不利于学习底层知识。过度依赖 unstorage 这种封装,可能会导致开发者忽略了底层存储方案的原理和最佳实践。对开发者的成长不利。
当然,以上只是一些可能的问题,不代表 unstorage 一定是一个坏的选择。但在决定是否引入 unstorage 时,你需要认真评估这些潜在的问题,权衡利弊,选择最适合自己项目的方案。
你提到了八条反对意见。我可以从不同角度进行反驳:一个是适配层开发者的角度,一个用户的角度和 sdk 提供方的角度。
- 从开发者角度看,我们可以约定审核、自动化测试、功能认证的方式来做质量把控和版本兼容
- 从用户角度看,建立版本升级机制,和自动化迁移方案可以家少问题,使用这种库回抹平细节,这个需要明确告知开发者,如果你依赖底层特性,依然需要学习原始内容。
- 从 sdk 提供方角度看,积极向上游反馈,建立自动化测试的案例,建立长效反馈机制能引入最佳实践和减少性能开销。
请你根据我说的继续挑战我
很好,你从适配层开发者、用户和 sdk 提供方三个角度提出了一些解决方案。但我认为这些方案还是存在一些问题和挑战:
- 适配层开发者角度
- 审核和认证机制可能会增加开发者的负担,降低了贡献的积极性。而且审核标准的制定和执行也需要投入人力物力。
- 自动化测试确实可以提高质量,但编写全面的测试用例本身也是一个挑战,需要开发者投入大量时间。而且一些边界情况和性能问题,自动化测试可能难以覆盖到。
- 用户角度
- 版本升级和自动化迁移方案可以减少用户的升级成本,但如果适配层之间存在较大的 API 差异,自动化迁移可能会很困难,甚至引入新的 bug。用户可能还是需要手动调整代码。
- 你提到如果用户依赖底层特性,还是需要学习原始内容。那么这种情况下,unstorage 的抽象是否就失去了意义?用户可能会质疑,不如直接使用原始的 sdk, 反而更简单直接。
- sdk 提供方角度
- 向上游反馈和建立长效机制,这些都需要 sdk 提供方的配合和投入。但并不是所有的 sdk 提供方都有足够的资源和意愿来支持 unstorage 这样的第三方抽象层。特别是一些封闭源代码的商业 sdk, 可能更难获得支持。
- 即使建立了反馈机制,sdk 提供方也不太可能为 unstorage 定制功能和优化性能,他们的优先级还是自己的 sdk。unstorage 只能被动地适应 sdk 的变化。
除了以上几点,我还想到一些其他的挑战:
- 学习曲线和生态割裂问题
- 即使有了文档和示例,开发者要熟悉 unstorage 的概念和 API, 以及各种适配层的用法,还是需要一定的学习曲线。而且不同适配层之间的差异,可能会导致开发者在项目中混用不同风格的代码,反而增加了维护成本。
- 不同项目和团队选择不同的适配层,可能会导致代码难以复用,社区生态出现割裂。
- 限制底层能力的发挥
- 有些存储方案可能有一些独特的功能,但这些功能很难通过 unstorage 抽象出来,或者抽象出来后失去了灵活性。这就限制了底层存储方案能力的发挥。
- 一些对性能敏感的项目,可能会非常关注存储方案的性能。而 unstorage 作为一个中间层,难免会引入一些性能开销,这可能会影响项目选型。
- 对低代码平台的影响
- 现在低代码平台也在兴起,其中一些平台会提供一些预置的存储连接器。而 unstorage 的出现,可能会干扰这些平台的设计,引入更多的复杂度。
当然,我提出的这些问题,也不是完全无解的。需要在实践中不断探索,权衡利弊,寻找最佳的平衡点。但在做决策时,你需要非常慎重地评估这些潜在的风险和挑战,不能只看到好的一面。
然后我把我的发言,丢给 GPT4
GPT4 这样回复
从反对意见方的角度出发,我可以提出以下几个潜在的担忧和反对理由:
1. 学习成本转移而非减少
虽然通过 unjs/unstorage 封装操作可以简化 API 的使用,但这种简化可能是表面的。用户仍然需要学习如何正确地使用 unstorage 库及其驱动程序。如果一个项目中要用到多种不同的数据源,那么用户可能需要理解多个驱动程序的不同 API 和概念。这可能导致学习成本从一个地方转移到另一个地方,而不是真正减少。
2. 一致性和可预测性问题
如果有多个驱动程序,保持它们的 API 风格和行为一致将是一个挑战。不同的开发者可能会有不同的实现方式,这可能导致 API 在不同的驱动程序之间行为不一致,从而增加了理解和调试的难度。
3. 性能和复杂性
添加额外的抽象层可能会带来性能开销。每一层的封装都可能引入延迟和资源消耗。此外,系统的复杂性可能会增加,当出现问题时,调试和解决问题可能会更困难,因为需要考虑额外的抽象层。
4. 驱动程序的质量和安全性
依赖于社区驱动的插件和适配层意味着项目的质量和安全性可能会受到影响。不是所有的开发者都会遵循最佳实践,审计和测试的不足可能会导致安全问题和缺陷。
5. 版本兼容性和升级问题
随着时间的推移,维护多个驱动程序的版本兼容性可能会变得复杂。如果底层 API 发生变化,所有的适配层都需要更新,用户可能会面临频繁的升级和迁移问题。
6. 锁定效应
如果`unstorage`及其适配层变得流行,用户可能会过度依赖这个生态系统,从而陷入技术锁定。一旦需要更换技术栈或者出现更好的解决方案,迁移可能会变得困难。
作为回应,您所提出的从不同角度的解决方案确实可以缓解这些问题。质量把控、版本兼容性策略以及与上游的良性互动确实是保证项目长期成功的关键。但最重要的是确保开发者社区的参与和贡献是在一个高标准和透明的环境中进行的,以此来维护整个生态系统的健康。
总结#
我认为是可行的,当多个库放在一起摆出来,规模效应行程的话,影响回更多。
对我个人而言,从普通用户变成深度用户、潜在的维护者、边缘问题的提出者,对我的技术积累也有成长。