找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 747|回复: 2

版本管理与接口设计规范

[复制链接]
发表于 2023-11-24 12:05:20 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×

接口的设计是必须跟随版本管理走的。

版本管理基础

设计制作软件的时候,应当按照规范的方式设计版本标签,请参考下图。

v001.png

概述

版本管理 并不需要非常死板,但是必须要有版本管理意识,通过 合理设计版本号 ,使协作开发人员可以判断你对程序软件的改动大小,并配合你的开发策略跟进开发。
开发人员改代码时,部分代码的改动是为了 修 bug,部分代码的改动是为了 新添功能,而还有的时候则是重构代码、把旧代码全部删了重新使用新的技术或者策略进行开发。在不同的改动情况下,版本号的递增方式需要经过妥善考虑后进行递增,以便于你的软件的使用者/对接者能够顺利对接你的软件。

如何合理设计版本号

此处例举我设计版本号时惯用的一套规则。

发布号的使用

开发过程中,软件已经可以使用,可能有 bug,但至少代码已经 封装好了、粗略调试可用、等待联调时,可打 Tag。每发布时,Tag 的发布号 递增
接口若发生变化,但是 仍能兼容旧的接口调用方式 时,可以使用发布号的递增。但如果接口不再兼容现有的接口调用方式时则需要增加小版本号。

gitlog.png

根据提交历史,在完成当前需求、发布软件时,打 Tag,并递增发布号。
改到一半、并不能运行的软件 不打 Tag。在代码改完、能 调试运行时,打 Tag。

小版本号的使用

当软件接口发生明显变化,或者软件设计策略发生变化后,发布新的软件时,使小版本号递增,并使 发布号归零
此处描述的设计策略包括 算法 的改动、单线程/多线程运行策略 的改动、是否使用 额外资源比如GPU或大量内存、磁盘容量等 进行计算的改动等。
总结一下通常情况下在此时升高小版本号:

  • 软件输出的数据格式发生改变
    • 比如输出的 JSON 文件的结构发生改变,旧的读 JSON 方式无法兼容。
  • 现有接口发生变化,不再兼容现有调用方式
    • 比如 删除 了接口函数。
    • 改变了接口函数的 参数返回值
    • 对于命令行程序,删除或改动命令行参数对应的软件行为。
    • 接口设计模式大幅变化,比如从 调用子过程方式进行接口调用变为 接口类继承、实现接口类成员函数。
  • 完全新增一套新接口
    • 比如新增 Web API。
    • 在已有 调用子过程方式实现功能的基础上,新添一整套的 接口类方式接口。
    • 一个 DLL 程序,原本只提供了纯 C 语言接口,改动后新增了针对 Java 的接口函数,可用 Java 类封装。
    • 一个只能通过读写本地 文件来工作的软件变为可以与其它软件直接 通讯交换文件内容。
  • 软件行为发生明显变化
    • 比如本来是执行完成任务后就 立即结束的软件变为内存 驻留待命的软件。
    • 从完全不会产生临时文件的软件变为会 产生临时文件的软件。
    • 从不需要额外的权限变为有时 需要额外的权限提升的软件。
    • 单线程软件变为 多线程软件。
    • 纯本地可运行软件变为 需要联网调用某 Web 接口后才能运行的软件。
    • 软件若需要按照 特定结构来使用文件系统时,文件目录结构发生变化。
  • 软件兼容性发生变化
    • 比如增加/减少了 系统软件依赖
    • 小幅度改变对 硬件性能的最低要求。
    • 增加/减少对 网络的依赖。
    • 增加/减少对系统已安装 字体的依赖。
  • 软件增加了新功能
    • 比如某图像压缩软件被设计为既可以压缩图像也可以压缩视频。
  • 软件使用的系统资源发生明显变化。
    • 比如从只使用 CPU 变为同时使用 CPU 和 GPU 的计算方式。
      基本上,小版本号主要用于 明确当前软件的执行时的各项前提要求。从系统设备资源的使用到接口依赖的需求。
      多端对接时,接口的调用需按照接口对应的版本来设计。

大版本号的使用

当软件发生了 大幅改动,尤其是发生了重构,许多 旧代码被删除,然后使用 新的技术或更换了 开发策略进行开发,或整个软件与其配套服务 逻辑框架发生变化时,软件发生大版本号的改动。

  • 典型例子1:Java 编程语言为了提升其功能和性能,方便性和安全性,对系统库和语法进行了改变,在此时 Java 语言的版本号进行 大版本号升级。使用 Java8 进行开发,和使用 Java9 进行开发的时候,开发者可以明显察觉 Java8 与 Java9 的差异,并且部分旧的 Java8 写法不再能在 Java9 里面使用。大版本号的变化,可以让使用者/对接者能够明确察觉到 软件使用策略的大幅变化,并且会专门研读文档中描述 Java8 与 Java9 之间差异的部分,来更好地使用软件。
  • 典型例子2:Python 编程语言在发展过程中不断增加新特性、改善其书写舒适度和代码美观度,不断增加小版本号,比如 Python2.6 经过改进后变为 Python 2.7,增加了新的语法特性。但是, Python2 在发展的过程中,人们发现一个无论如何也无法逃避的事实:现有的语法设计本身对 Python 语言的发展有较大的限制,比如:在面向对象编程语言的思维逻辑下,Python2 的语法并不完全尊重面向对象编程的思维逻辑,语法中有相当大的一部分内容不符合面向对象编程。于是,Python 的开发者们 重新设计了这款编程语言的语法逻辑,Python3 就诞生了。它的 定位并没有明显的改变:Python 的定位是 专用于偷懒的舒适的脚本语言。但是它的使用方式发生了较大的变化:整套语法都重新设计了。Python2 的使用者需要重新学习新的 Python3 的语法后才能使用 Python3,而任何一个使用 CMD/shell/bash 的苦难者在想要改变现状的时候,会发现不论是 Python2 还是 Python3 从定位上来说都很符合他的需求,但是因为 Python3 的大版本号是3,Python2 的大版本号是2,将来的趋势一定是 Python3 会变得越来越好,并且 Python3 的语法设计更加合理,是较为完备的面向对象编程语言,而 Python2 则不再得到维护,淹没于历史的海洋。于是初学者会选择 Python3 来学习使用,并使 Python3 的社区得到更好的发展,最终使 Python 编程语言可以成功从 Python2 时代过渡到 Python3 时代。

不过一般情况下一个软件开发者也可以 不使用大版本号,而是直接 开新坑,建立新工程,重新取名字,并找来完全不同的另一套依赖库,以及人马,针对某个业务场景进行全面优化。

接口设计规范

接口设计与软件版本挂钩。接口决定开发者之间的协作方式。协同开发时,不同的开发者之间必须约定好接口。

1. 设计接口时:需尽量多考虑接口会被以什么样的方式使用

做好接口参数合理性检查

  • 你的接口的使用者不一定是你的同事,他也可以是入侵进来的黑客/你的敌人/你的测试人员。
  • 当你的接口被人不慎进行错误调用时,你的程序不会因此崩溃/发生任何非设计性的行为(术语:Robust鲁棒性,或称 肉棒性)。

公私分明:做好隔离,避免调用者越过接口使用你的私有内部数据

  • 你在进行你自己的内部开发的时候,你会有需要频繁改动的代码。这部分代码做成私有,禁止外部直接访问。
  • 如果能隐藏私有的部分使其不可见,是上策
    • 比如 C 语言使用结构体 struct 来设计概念上的对象时,你可以用一个 void* internal; 成员指向你程序内部工作时需要用到的数据。
  • 如果不能隐藏私有部分,做好标记,让使用者知道这部分的数据别碰。
    • 比如 Python 可以在私有的函数和变量名字前面添加下划线 _,使其在外部不能被直接使用。
    • 比如 C++/C#/Java 可以使用 private 关键字。
  • 不希望被外界改动的数据需做成 只读属性。
    • C++ 没有 只读属性,因此 C++ 在这个地方通过设计 GetXxxxx() 这样的函数来获取数据。
    • C 语言同上。合理设计 getter/setter
    • Java 可以使用 final 关键字,但这会导致数据你在内部也无法改动。此时也靠使用 getter。

充分详细设计报错逻辑:用好异常机制或错误码机制,使你的调用者可以判断接口是否正常工作。

  • C++/C#/Java 设计接口时,一定要明确你会抛出的 所有的异常种类,便于调用者根据不同的异常类型进行处理。
    • C++ 的 std::map 或者 std:unordered_mapat() 方法在 Key 无效的时候并不会自动提示 Key 的值。需要做好封装,自己提示 Key 的值用于排错。
  • 使用错误码方式设计接口时,应尽量提供 错误描述字符串,便于打日志排查错误。
  • 命令行程序明确返回值、正常内容输出到 STDOUT、报错内容输出到 STDERR,程序正常执行时应返回 0 作为 ExitCode,参数错误时返回 1,程序遇到问题而报错退出时返回 -1,以便于你的程序可集成到 bash/Python 脚本等。
    • 调用命令行程序时应当读取命令行程序输出的所有内容包括 ExitCode、STDOUT 和 STDERR
    • Java 调用命令行程序时应同时读取 getInputStream()getErrorStream() 的数据。

编写文档:描述清楚你希望别人如何使用你的接口。

  • 文档一定要包含 接口的版本,针对不同版本的接口需要编写 不同的文档
  • 具体描述接口参数如何传递、如何使用返回值、什么样的参数有效、什么样的参数会导致错误。
  • 具体描述接口内可能发生的功能实现行为,尤其是以下几点:
    • 能否多线程调用,是否需要调用者自己上锁。
    • 你内部是否使用了锁。
    • 你大致是如何实现你的功能的。让调用者心里有个数。注意不能明确提到接口内部 私有部分的使用。
    • 抛出异常的原因,以及所有的异常种类。
  • 如何排错,以及排错案例。

编写 Demo 例程,让调用者可以 CV 你的代码来调用你的接口。

Demo 例程也应当包含充分的文档,去描述为何接口要如此调用。

2. 发布接口前三思:需尽量全面考虑其是否会影响后续的开发

接口一旦明确下来,对接的开发者们各自将针对接口进行自己的软件开发。接口功能提供者提供具体的功能实现,而接口功能的调用者则按照 接口文档对接口进行调用。

适当确保灵活性,留好所有可能需要的参数的入口

一定要留好所有的坑点,避免常量、字段数值写死,并保留余地使 接口在不发生变化时你可以给你的程序 添加新的功能或者除错。
发布前,尽量和对接的开发者进行协商,了解对方在使用时 可能的需求。留好你的坑位,在这些需求真正到来时,你可以减少对接口的改动。
注:灵活性不能太高,一旦高于接口对调用者的约束,你的接口设计将 **形同虚设。

3. 尽量避免对接口的设计进行改变

不到万不得已,不应改变现有接口的功能。 可以增加新的接口,但是不能减少旧的接口

  • 如果某接口函数的内部实现已经是不需要的了,则你依然无论如何都要提供这个函数,并使其内部没有任何行为,以确保你的接口符合调用者所理解的逻辑。
    一旦发生接口变动,需要和所有对接者进行及时的沟通,并及时 更新接口文档以便于对接者查阅。
    接口变动影响软件对接时,应当更新软件的 小版本号以体现 软件运行需求的明显变化。
回复

使用道具 举报

发表于 2024-1-24 23:07:21 | 显示全部楼层
设计接口时,有没有可能量化地描述下列三个要求之间的权衡?
1. 减少通信压力;
2. 减少后台计算压力,尽量将一些校验留给前端;
3. 可靠性;

比如单词调用发送的数据量每降低1个单位,计算压力也会提高1个单位;然后可靠性每提高1个单位,计算压力也会提高1个单位,然后想办法求一个帕累托最优。
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2024-2-8 00:50:47 | 显示全部楼层
云宝黛锡 发表于 2024-1-24 23:07
设计接口时,有没有可能量化地描述下列三个要求之间的权衡?
1. 减少通信压力;
2. 减少后台计算压力,尽量 ...

1. 可以考虑省去不必要的通信,但是必要的通信是无法省去的。
2. 出于信息安全考虑,前端过来的数据一律视为不可信任。
3. 请你具体描述一下“可靠性”。
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-23 17:27 , Processed in 0.051550 second(s), 29 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表