搭建 Restful Web 服务

1. 理解 REST

  REST 全称是 Representational State Transfer,中文意思是表征性状态转移。它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。值得注意的是REST并没有一个明确的标准,而更像是一种设计的风格。如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

  理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。

1.1. REST 原则

  • 资源 可通过目录结构样式的 URIs 暴露

  • 表述 可以通过 JSON 或 XML 表达的数据对象或属性来传递

  • 消息 使用统一的 HTTP 方法(例如:GET、POST、PUT 和 DELETE)

  • 无状态 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息

    1.2. HTTP 方法

      使用 HTTP 将 CRUD(create, retrieve, update, delete <创建、获取、更新、删除—增删改查>)操作映射为 HTTP 请求。如果按照HTTP方法的语义来暴露资源,那么接口将会拥有安全性和幂等性的特性,例如GET和HEAD请求都是安全的, 无论请求多少次,都不会改变服务器状态。而GET、HEAD、PUT和DELETE请求都是幂等的,无论对资源操作多少次, 结果总是一样的,后面的请求并不会产生比第一次更多的影响。

    1.2.1. GET

  • 安全且幂等

  • 获取信息

    1.2.2. POST

  • 不安全且不幂等

  • 使用请求中提供的实体执行操作,可用于创建资源或更新资源

    1.2.3. PUT

  • 不安全但幂等

  • 使用请求中提供的实体执行操作,可用于创建资源或更新资源

    1.2.4. DELETE

  • 不安全但幂等

  • 删除资源
      POST和PUT在创建资源的区别在于,所创建的资源的名称(URI)是否由客户端决定。 例如为我的博文增加一个java的分类,生成的路径就是分类名/categories/java,那么就可以采用PUT方法。不过很多人直接把POST、GET、PUT、DELETE直接对应上CRUD,例如在一个典型的rails实现的RESTful应用中就是这么做的。

    1.3. HTTP status codes

      状态码指示 HTTP 请求的结果:

  • 1XX:信息

  • 2XX:成功

  • 3XX:转发

  • 4XX:客户端错误

  • 5XX:服务端错误

    1.4. 媒体类型

      HTTP头中的 Accept 和 Content-Type 可用于描述HTTP请求中发送或请求的内容。如果客户端请求JSON响应,那么可以将 Accept 设为 application/json。相应地,如果发送的内容是XML,那么可以设置 Content-Type 为 application/xml 。

    2. REST API 设计最佳实践

      这里介绍一些设计 REST API 的最佳实践,大家先记住下面这句话:

    URL 是个句子,其中资源是名词、HTTP 方法是动词。

    2.1. 使用名词来表示资源

      下面是一些例子:

  • GET – /users:返回用户列表

  • GET – /users/100:返回一个特定用户

  • POST – /users:创建一个新用户

  • PUT – /users/200:更新一个特定用户

  • DELETE – /users/711:删除一个特定用户
      不要使用动词:

  • /getAllsers

  • /getUserById

  • /createNewUser

  • /updateUser

  • /deleteUser

    2.2 在 HTTP 头中使用适当的序列化格式

      客户端和服务端都需要知道通信所用的格式,这个格式要在 HTTP 头中指定:

  • Content-Type 定义请求格式

  • Accept 定义一个可接受的响应格式列表

    2.3 Get 方法和查询参数不应当改变状态

      使用 PUT, POST 和 DELETE 方法来改变状态,不要使用 GET 方法来改变状态:

  • GET /users/711?activate

  • GET /users/711/activate

    2.4. 使用子资源表示关联

      如果一个资源与另一个资源关联,使用子资源:

  • GET /cars/711/drivers/ 返回711号汽车的驾驶员列表

  • GET /cars/711/drivers/4 返回711号汽车的第4号驾驶员

    2.5. 使用适当的 HTTP 方法 (动词)

      再回顾一下这句话:

    URL 是个句子,其中资源是名词、HTTP 方法是动词。

  • GET:获取在URI资源中指定的表述,响应消息体包含所请求资源的细节。

  • POST:创建一个URI指定的新资源,请求消息体提供新资源的细节。注意,POST也可以触发一些操作,而不一定是要创建新资源。

  • PUT:创建或替代指定URI的资源。请求消息体指定要创建或更新的资源。

  • DELETE:移除指定URI的资源。

    2.6. HTTP 响应状态码

      当客户端通过API向服务端发起一个请求时,客户端应当知道反馈:是否失败、通过或者请求错误。HTTP 状态码是一批标准化代码,在不同的场景下有不同的解释。服务器应当总是返回正确的状态码。
      下面是重要的HTTP代码分类:

  • 2xx (成功分类):这些状态码代码请求动作被接收且被服务器成功处理。

    • 200:Ok 表示 GET、PUT 或 POST 请求的标准状态码。
    • 201:Created(已创建)表示实例已被创建,多用于 POST 方法。
    • 204:No Content(无内容)表示请求已被成功处理但没有返回任何内容。常用于 DELETE 方法返回。
  • 3xx (转发分类)

    • 304:Not Modified(无修改)表示客户端已经缓存此响应,无须再次传输相同内容。
  • 4xx (客户端错误分类):这些状态码代表客户端提交了一个错误请求。

    • 400:Bad Request(错误请求)表示客户端请求没被处理,因为服务端无法理解客户端请求。
    • 401:Unauthorized(无授权)表示客户端无权访问资源,应当加上所需的认证信息后再次请求。
    • 403:Forbidden(禁止访问)表示请求有效且客户端已获授权,但客户端无权访问该资源。
    • 404:Not Found(没发现)表示所请求的资源现在不可用。
    • 410:Gone(移除)表示所请求的资源已被移除。
  • 5xx (服务端错误分类)

    • 500:Internal Server Error(内部服务器错误)表示请求有效,但是服务端发生了异常。
    • 503:Service Unavailable(服务不可用)表示服务器关闭或不可用,通常是指服务器处于维护状态。

      2.7. 名称规约

        你可以遵循任何名称规约,只要保持跨应用一致性即可。如果请求体和响应体是 JSON 类型,那么请遵循驼峰名称规约。

      2.8. 搜索、排序、过滤与分页

        上面一些示例都是在一个数据集上的简单查询,对于复杂的数据,我们需要在 GET 方法 API 上加一些参数来处理。下面是一些示例:

  • 排序:这个例子中,客户想获取排序的公司列表,GET /companies 应当在查询时接受多种排序参数。譬如 GET /companies?sort=rank_asc 将以等级升序的方式对公司进行排序。

  • 过滤:要过滤数据集,我们可以通过查询参数传递不同的选项。比如 GET /companies?category=banking&location=india 将过滤分类为银行且位于印度的公司。

  • 搜索:在公司列表中搜索公司名的 API 端点应当是 GET /companies?search=Digital。

  • 分页:当数据集太大时,我们应当将数据集分割成小的数据块,这样有利于提升服务端性能,也方便客户端处理响应。如 GET /companies?page=23 意味着获取公司列表的第 23 页数据。

    2.9. Restful API 版本

      一般使用不带点的简单数字表示版本,数字前加字母v代表版本号,如下所示:

  • /blog/api/v1

  • http://api.yourservice.com/v1/companies/34/employees

    2.10. 处理 JSON 错误体

      API 错误处理机制是很重要的,而且要好好规划。极力推荐总是在返回字段中包含错误消息。一个 JSON 错误体应当为开发者提供一些有用的信息:错误消息、错误代码以及详细描述。下面是一个较好的示例:

    {
    "code": 1234,
    "message": "Something bad happened :(",
    "description": "More details about the error here"
    }

    2.11. 如何创建 Rest API URL

      推荐使用下面格式的 URL:

  • http(s)://{域名(:端口号)}/{表示REST API的值}/{API版本}/{识别资源的路径}

  • http(s)://{表示REST API的域名(:端口号)}/{API 版本}/{识别资源的路径}
      如下所示:

  • http://example.com/api/v1/members/M000000001

  • http://api.example.com/v1/members/M000000001

    3. 开发基于 Spring Boot 的 Restful Web 服务

      Spring Boot 提供了构建企业应用中 RESTful Web 服务的极佳支持。

    3.1. 引入依赖

      要构建 RESTful Web 服务,我们需要在构建配置文件中加上 Spring Boot Starter Web 依赖。
      对于 Maven 用户,使用以下的代码在 pom.xml 文件中加入依赖:

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>    
    </dependency>

      对于 Gradle 用户,使用以下的代码在 build.gradle 文件中加入依赖:

    compile('org.springframework.boot:spring-boot-starter-web')

    3.2. Rest 相关注解

      在继续构建 RESTful web 服务前,建议你先要熟悉下面的注解:

    Rest Controller

      @RestController 注解用于定义 RESTful web 服务。它提供 JSON、XML 和自定义响应。语法如下所示:

    @RestController
    public class ProductServiceController {
    }

    Request Mapping

      @RequestMapping 注解用于定义请求 URI 以访问 REST 端点。我们可以定义 Request 方法来消费 produce 对象。默认的请求方法是 GET:

    @RequestMapping(value = "/products")
    public ResponseEntity<Object> getProducts() { }
    Request Body
    @RequestBody 注解用于定义请求体内容类型。
    public ResponseEntity<Object> createProduct(@RequestBody Product product) {
    }

    Path Variable

      @PathVariable 注解被用于定义自定义或动态的请求 URI,Path variable 被放在请求 URI 中的大括号内,如下所示:

    public ResponseEntity<Object> updateProduct(@PathVariable("id") String id) {
    }

    Request Parameter

      @RequestParam 注解被用于从请求 URL 中读取请求参数。缺省情况下是必须的,也可以为请求参数设置默认值。如下所示:
    public ResponseEntity getProduct(
    @RequestParam(value = "name", required = false, defaultValue = "honey") String name) {
    }

    3.3. 编写 REST API

    GET API

      下面的示例代码定义了 HTTP GET 请求方法。在这个例子里,我们使用 HashMap 来在存储 Product。注意我们使用了 POJO 类来存储产品。
      在这里,请求 URI 是 /products,它会从 HashMap 仓储中返回产品列表。下面的控制器类文件包含了 GET 方法的 REST 端点:

    
    package com.tutorialspoint.demo.controller;

    import java.util.HashMap;
    import java.util.Map;

    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import com.tutorialspoint.demo.model.Product;

    @RestController
    public class ProductServiceController {
    private static Map<String, Product> productRepo = new HashMap<>();
    static {
    Product honey = new Product();
    honey.setId("1");
    honey.setName("Honey");
    productRepo.put(honey.getId(), honey);

      Product almond = new Product();
      almond.setId("2");
      almond.setName("Almond");
      productRepo.put(almond.getId(), almond);

    }
    @RequestMapping(value = "/products")
    public ResponseEntity<Object> getProduct() {
    return new ResponseEntity<>(productRepo.values(), HttpStatus.OK);
    }
    }

    ### POST API
      HTTP POST 请求用于创建资源。这个方法包含请求体。我们可以通过发送请求参数和路径变量来定义自定义或动态 URL。
      下面的示例代码定义了 HTTP POST 请求方法。在这个例子中,我们使用 HashMap 来存储 Product,这里产品是一个 POJO 类。
      这里,请求 URI 是 /products,在产品被存入 HashMap 仓储后,它会返回字符串。
    ```java
    package com.tutorialspoint.demo.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.tutorialspoint.demo.model.Product;
    
    @RestController
    public class ProductServiceController {
       private static Map<String, Product> productRepo = new HashMap<>();
    
       @RequestMapping(value = "/products", method = RequestMethod.POST)
       public ResponseEntity<Object> createProduct(@RequestBody Product product) {
          productRepo.put(product.getId(), product);
          return new ResponseEntity<>("Product is created successfully", HttpStatus.CREATED);
       }
    }

    PUT API

      HTTP PUT 请求用于更新已有的资源。这个方法包含请求体。我们可以通过发送请求参数和路径变量来定义自定义或动态 URL。
      下面的例子展示了如何定义 HTTP PUT 请求方法。在这个例子中,我们使用 HashMap 更新现存的产品。此处,产品是一个 POJO 类。
      这里,请求 URI 是 /products/{id},在产品被存入 HashMap 仓储后,它会返回字符串。注意我们使用路径变量 {id} 定义需要更新的产品 ID:

    package com.tutorialspoint.demo.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    import com.tutorialspoint.demo.model.Product;
    
    @RestController
    public class ProductServiceController {
       private static Map<String, Product> productRepo = new HashMap<>();
    
       @RequestMapping(value = "/products/{id}", method = RequestMethod.PUT)
       public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody Product product) {
          productRepo.remove(id);
          product.setId(id);
          productRepo.put(id, product);
          return new ResponseEntity<>("Product is updated successsfully", HttpStatus.OK);
       }   
    }

    DELETE API

      HTTP Delete 请求用于删除存在的资源。这个方法不包含任何请求体。我们可以通过发送请求参数和路径变量来定义自定义或动态 URL。
      下面的例子展示如何定义 HTTP DELETE 请求方法。这个例子中,我们使用 HashMap 来移除现存的产品,用 POJO 来表示。
      请求 URI 是 /products/{id} 在产品被从 HashMap 仓储中删除后,它会返回字符串。 我们使用路径变量 {id} 来定义要被删除的产品 ID。

    package com.tutorialspoint.demo.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.tutorialspoint.demo.model.Product;
    
    @RestController
    public class ProductServiceController {
       private static Map<String, Product> productRepo = new HashMap<>();
    
       @RequestMapping(value = "/products/{id}", method = RequestMethod.DELETE)
       public ResponseEntity<Object> delete(@PathVariable("id") String id) {
          productRepo.remove(id);
          return new ResponseEntity<>("Product is deleted successsfully", HttpStatus.OK);
       }
    }

    软件开发中的常见的15个定律和原则释义及应用

    在围绕软件开发的讨论中,几乎不可能避免引用一两条定律。

    “这行不通,因为‘X法则’!” 你可能听过人们说。或者“你不知道‘Y原则’吗? 你是哪种软件开发人员?”。

    有许多法律和原则可以引用,其中大部分都基于真理。然而,盲目地使用像上面这样的绝对陈述来应用它们肯定会导致自负和失败。

    本文列举了一些可以应用于软件开发的最流行的规律和原则。对于每条规律,我们将快速讨论其主要命题,然后探讨如何将其应用于软件开发(也许何时不应该)。

    帕累托原则(80/20 规则)

    解释

    帕累托原则指出,通常 80% 的结果来自 20% 的原因。数字 80 和 20 无论如何都不是精确的,但该原理的总体思路是结果通常分布不均。

    我们可以在生活的许多领域遵守这条规则,例如:

    • 世界上最富有的 20% 的人创造了世界上 80% 的收入,
    • 80%的犯罪是由20%的罪犯所为
    • 自 2020 年以来,我们知道 80% 的病毒传播来自 20% 的受感染人群。

    在软件开发中的应用

    我们可以从帕累托原则中获得的主要好处是专注。它可以帮助我们专注于重要的事情(20%),而不是在不重要的事情(其他 80%)上浪费时间和精力。不重要的事情对我们来说似乎很重要,因为有太多(而且看起来很紧急)。但最好的结果往往是通过专注于重要的少数来实现的。

    在软件开发中,我们可以基于这个原则来专注于构建正确的功能,例如:

    • 专注于构成产品价值 80% 的 20% 的产品功能,
    • 专注于导致 80% 用户沮丧的 20% 错误,
    • 专注于 80% 的产品功能需要 20% 的总时间来构建,
    • ……

    只是问“现在最重要的事情是什么?” 能够帮助你完成下一个最重要的事情,而不是下一个最紧急的事情。

    顺便说一下,敏捷和 DevOps 等现代开发方法有助于获得这种关注!具有定期用户反馈的快速迭代允许对重要事项进行数据驱动的决策。诸如基于主干的带有功能标记的开发(例如使用 LaunchDarkly)之类的实践可以帮助软件团队实现目标。

    破窗定理

    解释

    一扇被打破的窗户会招来恶意破坏,所以用不了多久,所有的窗户都被打破了。

    一般来说:混乱会带来更多的混乱

    如果我们的环境是原始的,我们就会有动力保持这种状态。环境中的混乱越多,我们添加混乱的门槛就越低。毕竟已经混乱了……谁在乎我们是否再添加一点呢?

    我们可以从这条规则中获得的主要好处是我们应该意识到我们周围的混乱。如果人们习惯于它,不再关心它了,那么最好为混乱带来一些秩序。

    在软件开发中的应用

    在软件开发中,我们可以将其应用于代码质量:我们引入代码库的每一种代码异味都会降低我们添加更多代码异味的门槛。我们应该 [[开始清理]] 并保持代码库干净以避免这种情况发生。许多代码库如此难以理解和维护的原因是,破窗已经悄然出现并且没有足够快地修复。

    我们也可以将这个原则应用到测试覆盖率上:一旦有一定数量的代码进入了未被测试覆盖的代码库,就会添加更多未被覆盖的代码。这是保持 100% 代码覆盖率(应该覆盖的代码的)的论据,因此我们可以在窗口破裂之前看到裂缝。

    奥卡姆剃刀

    解释

    剃刀哲学是一种原理,它通过消除(或“削除”)不可能的解释来帮助解释某些事情。

    奥卡姆剃刀指出,如果有多个假设,我们应该选择假设最少的假设(这很可能是解释最简单的假设)。

    在软件开发中的应用

    我们可以在事件分析中应用奥卡姆剃刀。您可能遇到过这样的情况:用户报告了您的应用程序存在问题,但您不知道导致问题的原因。因此,您搜索日志和指标,试图找到根本原因。

    下次用户报告错误时,维护一个事件调查文档。写下您对导致问题的原因的假设。然后,对于每个假设,列出事实和假设。如果一个假设被证明是正确的,则将其标记为事实。如果某个假设被证明是错误的,请将其从文档中删除或将其标记为错误。在任何时候,您都可以将时间集中在最可能的假设上,而不是浪费时间寻找不相干的东西。

    达克效应

    解释

    邓宁-克鲁格效应表明,没有经验的人往往会高估自己的能力,而有经验的人往往会低估自己的能力

    如果你不擅长某件事,你会认为你擅长它。如果你擅长某事,你认为你不擅长 – 这可能导致骗子综合症,这让你非常怀疑自己的能力,以至于你在其他具有相似技能的人中感到不舒服 – 不必要地害怕被质疑是一个骗子。

    在软件开发中的应用

    意识到这种认知偏差已经是朝着正确方向迈出的重要一步。它将帮助您更好地评估自己的技能,以便您可以寻求帮助,或克服自我怀疑并自己动手。

    有助于消除达克效应和骗子综合症的一种做法是结对或群体编程。你不是独自工作,沉浸在自我怀疑或优越感中,而是与其他人密切合作,边工作边交流思想、学习和教学。

    不过,这只适用于安全的环境。在个人主义被美化的环境中,结对或群体编程会导致更多的自我怀疑或更多的优越感妄想。

    彼得原则

    解释

    彼得原则指出,只要你成功,你就会得到晋升,直到你最终得到一份你不胜任的工作。由于您不再成功,您将不再获得晋升,这意味着您将生活在一份不会给您带来满足感或成功的工作中,通常这种感觉将在一直伴随在您的余生。

    前景黯淡。

    在软件开发中的应用

    在软件开发中,当您将角色从开发人员职业转换为管理职业时,彼得原则通常适用。然而,成为一名优秀的开发人员并不一定意味着你是一名优秀的经理。或者,您可能是一名优秀的经理,但却不能从经理工作中获得开发工作中所能获得的满足感,这意味着您没有全力以赴(这就是我的情况)。在任何情况下,你都很悲惨,在你面前的职业道路上看不到任何未来的发展。

    在这种情况下,退后一步,决定你想要什么样的职业生涯。然后,转换角色(或公司,如果需要)以获得您想要的角色。

    帕金森定律

    解释

    帕金森定律指出,工作总是会占据分配给它的时间。如果您的项目在两周的截止日期,则该项目将不会在此之前完成。 可能需要更长的时间,是的,但绝不会少于我们为它分配的时间,因为我们正在用不必要的工作或拖延来填补时间。

    在软件开发中的应用

    帕金森定律的主要驱动因素是:

    • 拖延症(“截止日期太远了,所以我现在不需要赶时间……”),还有
    • 范围蔓延(“当然,我们可以添加这个小功能,它不会花费我们太多时间……”)。

    为了战胜拖延症,我们可以把最后期限设置为几天而不是几周或内个月。在接下来的 2-3 天内需要做什么才能朝着目标前进?一个(健康的!)截止日期可以给我们足够的动力,让我们不要陷入拖延症的泥潭。

    为了防止范围蔓延,我们应该非常清楚地知道我们想要通过项目实现什么。成功的衡量标准是什么? 这个新功能是否会增强这些指标?那么如果每个人都明白这项工作需要更长的时间,我们应该添加它。如果新功能不符合使命,那就不用管它。

    霍夫施塔特定律

    解释

    霍夫施塔特定律指出:“即使考虑了霍夫施塔特定律,它所花的时间也比你预期的长”。

    即使您了解了这条法律,并增加了项目的时间分配,它仍然会比您预期的要长。这与帕金森定律密切相关,即工作总是会填满分配给它的时间。只是霍夫施塔特定律说它填充的时间超过了分配的时间。

    这条定律得到了心理学的支持。我们容易犯所谓的“计划谬误”,即在估算工作量时,我们通常不会考虑所有可用信息,即使我们认为我们已经考虑了。我们的估计几乎总是主观的,很少是正确的。

    在软件开发中的应用

    在软件开发(以及任何其他基于项目的工作)中,我们人类的乐观情绪发挥了很大作用。评估几乎总是过于乐观。

    为了减少霍夫施塔特定律的影响,我们可以尝试尽可能客观地进行估计。

    写下关于项目的假设和事实。将每个项目标记为假设或事实,以使数据质量可见并管理预期。

    不要依赖直觉,因为每个人的感受都不一样。写下估算值,让你的大脑思考它们。将它们与其他人的估计进行比较,然后讨论差异。

    即便如此,它仍然只是一个估计,很可能不能反映现实。如果估算不是基于统计数据或其他历史数据,那么它的价值就非常低,因此与要求您估算的人一起管理预期总是好的——这总是会出错的。如果你让它尽可能客观,它就会减少错误。

    康威定律

    解释

    康威定律指出,一个组织创建的任何系统都与该组织的团队和沟通结构相似。系统将在构建系统的团队有接口的地方具备接口。如果你有 10 个团队在一个系统上工作,你很可能会得到 10 个相互通信的子系统。

    在软件开发中的应用

    我们可以应用所谓的逆康威策略:创建最能支持我们想要构建的系统架构的组织结构。

    没有固定的团队结构,而是要有足够的灵活性来创建和解散团队,这对系统的当前状态是最好的。

    墨菲定律

    解释

    墨菲定律说,任何可能出错的事情,都会出错。它经常在意外发生后被引用。

    在软件开发中的应用

    软件开发是一个容易出错的职业。出错的主要来源是错误。没有任何一款软件不存在漏洞或事故,从而考验测试用户的耐心。

    我们可以通过在日常软件开发实践中养成减少错误影响的习惯来抵御墨菲定律。我们无法完全避免错误,但我们可以而且应该减少它们对用户的影响。

    对抗墨菲定律最有用的做法是特征标记。如果我们使用像 LaunchDarkly 这样的功能标记平台,我们可以在功能标记后面将更改部署到生产中。然后,我们可以使用有针对性的推出来激活内部 dogfooding 的标志,然后为少量友好的 Beta 用户激活它,最后将其发布给所有用户。这样,我们可以从越来越挑剔的用户群体那里获得关于变更的反馈。如果更改出错(并且在某些时候会出错)影响就很小,因为只有一小部分用户组会受到它的影响。而且,该标志可以快速关闭。

    布鲁克定律

    解释

    在经典著作《人月神话》中,弗雷德·布鲁克 (Fred Brook) 有句名言:为延期的项目增加人力会使项目延期更多

    尽管本书讨论的是软件项目,但它适用于大多数类型的项目,甚至是软件开发之外的项目。

    添加人员不会提高项目速度的原因是项目的通信开销随着添加到项目中的每个人呈指数增长。2 个人有 1 条通信路径,5个人已经有 5! = 120 条可能的通信路径。新人安顿下来并确定他们需要的沟通路径需要时间,这就是为什么在项目中添加新人时,迟到的项目会更晚。

    在软件开发中的应用

    很简单。改变截止日期,而不是在已经延期的项目中增加人力。

    对于向软件项目中增加新人的期望要切合实际。将人员添加到项目中可能会在某个时候提高速度,但并非总是如此,当然也不是立竿见影。人员和团队需要时间来适应日常工作,而在某些时候,工作无法充分并行化,因此增加更多人是没有意义的。 仔细考虑一个新人应该完成什么任务,以及在将该人添加到项目中时您期望什么。

    波斯特定律

    解释

    波斯特定律也被称为稳健性原则,它指出你应该“在你所做的事情上保守,在你接受别人的事情上自由”。

    换句话说,您可以接受多种不同形式的数据,以使您的软件尽可能灵活,但您在处理这些数据时应该非常小心,以免因无效或恶意数据而损害您的软件。

    在软件开发中的应用

    该定律源于软件开发,因此非常适于直接使用。

    为了增强健壮性,您的软件与其他软件或人之间的接口应允许不同形式的输入:

    • 为了向后兼容,新版本的接口应该接受旧版本和新版本的数据,
    • 为了更好的用户体验,UI 中的表单应该接受不同格式的数据,这样用户就不必担心格式。

    但是,如果我们愿意接受不同格式的数据,我们在处理这些数据时就必须保守。我们必须审查无效值,并确保我们不会因为允许太多不同的格式而损害系统的安全性。SQL 注入是一种可能的攻击,它是通过对用户输入过于宽松而启用的。

    克希霍夫原理

    解释

    克希霍夫原理指出,加密系统应该是安全的,即使它的方法是公知的。只有您用来解密某些东西的密钥才需要是私有的。

    在软件开发中的应用

    这很简单,真的。永远不要相信要求其方法是私有的加密系统。这被称为“隐藏的安全”。像这样的系统本质上是不安全的。一旦该方法向公众公开,它就容易受到攻击。

    相反,依靠公开审查和可信的对称和非对称加密系统,这些系统是在可以公开审查的开源包中实现的。每个想知道他们内部如何工作的人都可以查看代码并验证它们是否安全。

    莱纳斯定律

    解释

    在关于 Linux 内核开发的《教堂与集市》一书中,埃里克·雷蒙德 (Eric Raymond) 写道:“只要有足够的眼光,所有 bug 都是微不足道的”。他将此称为“莱纳斯定律”以纪念莱纳斯·托瓦兹。

    意思是,如果很多人看代码,那么相比很少人看代码而言,可以更好地揭露代码中的错误。

    在软件开发中的应用

    如果您想摆脱 bug,请让其他人查看您的代码。

    源于开源社区的一种常见做法是让开发人员提出包含代码更改的拉取请求(pull request),然后让其他开发人员在将拉取请求合并到主分支之前审查该拉取请求。这种做法也进入了闭源开发,但根据 Linus 定律,拉取请求在闭源环境(只有少数人查看它)中的作用不如在开源环境中(其中 可能很多贡献者都在看它)。

    其他为代码添加更多眼球的做法是结对编程和群体编程。至少在闭源环境中,这些在避免错误方面比拉取请求审查更有效,因为每个人都参与了代码的初始阶段,这为每个人提供了更好的上下文来理解代码和潜在的错误。

    沃斯定律

    解释

    沃斯定律指出,软件变慢的速度比硬件变快的速度要快

    在软件开发中的应用

    不要依赖强大的硬件来运行性能不佳的代码。相反,代码要加强性能优化。

    这必须与 [[软件开发定律#Knuth 的优化原则]] 的格言相平衡,该格言说“过早的优化是万恶之源”。要把精力花在为用户构建新功能上,而不是用于代码的性能优化上。

    通常,这是一种平衡的艺术。

    克努斯**优化原则**

    解释

    唐纳德·克努斯 (Donald Knuth) 在他的一部作品中写下了“过早优化是万恶之源”这句话,这句话经常断章取意,并被用作根本不关心优化代码的借口。

    在软件开发中的应用

    根据克努斯定律,我们不应该浪费精力过早地优化代码。然而,根据沃斯定律,我们也不应该依赖硬件足够快来执行未经优化的代码。

    最后,这就是我从这些原则中得出的结论:

    • 优化可以轻松完成且不需要太多努力的代码:例如,编写几行额外代码以避免经历可能有很多项的循环
    • 优化一直在执行的代码路径中的代码
    • 除此之外,不要在优化代码上花太多精力,除非你已经确定了一个性能瓶颈。

    保持怀疑

    定律和原则是好的。它允许我们从某个角度评估某些情况,如果没有它们,我们可能不会有这些情况。

    然而,盲目地将法律和原则应用于每种情况是行不通的。每一种情况都会带来微妙的变化,这可能意味着某个原则不能或不应该被应用。

    对你遇到的原则和定律保持怀疑。世界并不是非黑即白的。


    本文译自: Laws and Principles of Software Development – Reflectoring

    2022年软件开发趋势:远程工作已成主流

    2020 年 3 月,工作世界发生了翻天覆地的变化。到 2020 年 4 月,大约一半的公司报告称,由于新冠病毒,其 80% 以上的员工在家工作。大多数人再也没有回到办公室——远程工作将继续存在。

    被迫在网上生活,技术变得至关重要。 数字化转型现在是任何想要跟上步伐的组织的基本要求。以前就很抢手的技术工作者,现在更受追捧,以帮助建立一个我们都依赖技术进行最基本活动的世界。

    2022年技术将如何支持远程工作

    Gartner 的一项调查显示,由于新冠病毒,69% 的董事会加速了数字化转型。这一趋势将继续存在,组织的重点是非接触式服务 (60.1%)、迁移到云 (52.25%) 以及 DevOps 活动 (51.75%)。

    根据数据,我们预测了2022年的一些软件发展趋势:

    1. 云将变得越来越重要

    云将在 2022 年及未来几年在技术中发挥越来越重要的作用。可以迁移到云端的所有东西都将迁移到云端。

    以公司新开发人员的入职为例。他们通常会花费几周的时间来尝试让所有东西都在本地机器上运行。这非常耗时,不仅对于新员工来说,而且对于需要在此过程中提供帮助的经验丰富的工程师来说也是如此。

    到目前为止,大多数自动化构建、模拟环境和正在运行的生产应用程序已经迁移到云端。下一步是本地开发环境。

    微软和亚马逊已经开始着手这方面的工作,并且在 2021 年都发布了解决方案(微软的 GitHub Codespaces 和亚马逊的 AWS Cloud9),这些解决方案可以在几秒钟内提供可在浏览器上访问的开发环境。

    2. DevOps 将发挥重要作用

    谷歌的 DORA 进行的研究表明,“优秀的执行工程组织实现其组织目标的可能性是一般组织的两倍,并在三年内实现了 50% 的高增长率”。

    为了加快管道并快速交付新功能,团队需要确保他们的流程和工具尽可能好,消除障碍和瓶颈。因此,DevOps 和实现持续交付的实践至关重要。

    3. 人工智能辅助开发

    2021 年,我们已经见证了人工智能开始进入开发工具。GitHub Copilot、IBM AI for Code 和 Oracle 的新查询语言生成器是指向 AI 辅助开发方向的一些创新。

    2022 年,Forrester “预计几乎所有开发工具中都会出现 AI 机器人,为开发人员的工具箱添加自然语言和其他功能”。

    4. 低代码平台的崛起

    2017 年,《福布斯》将低代码平台归类为“极具颠覆性”,而且这一趋势还在加速。Gartner 预测:“到 2022 年,低代码应用平台预计仍将是低代码开发技术市场的最大组成部分,比 2020 年增长近 30%,到 2021 年达到 58 亿美元”。并补充说“到 2024 年,低代码 应用开发将占应用开发活动的 65% 以上。”

    公司如何支持远程工作者?

    “新常态”将继续存在。但这对雇主和雇员意味着什么?

    研究表明,拥有积极体验的远程员工的工作效率提高了 28%,敬业度提高了 46%。对公司来说有一个明显的好处:那些提供优质远程员工体验的公司可以提高 25% 的利润,降低 37% 的流失率。

    这些数字看起来很乐观,但它们仅适用于远程工作员工体验是正确的,并作为公司的优先考虑。但是如何才能做到这一点呢?

    管理团队工作量的技巧

    远程工作带来了许多挑战,并加剧了雇主和雇员之前已经面临的斗争。 研究表明,在2020 年,71% 的员工经历过倦怠,87% 的员工不得不加班。

    倦怠远非个人问题。仅在美国,职业倦怠员工的心理和身体问题每年就花费 125 至 1900 亿美元的医疗保健费用。再加上生产力的下降、高流动率和组织中人才的流失。 而且,最重要的成本是员工的福祉。

    为了解决这个问题,公司需要掌握工作量管理。但这不仅仅是为了确保您的员工不会过度劳累。相反,它是关于在团队之间战略性地分配工作以实现尽可能高的生产力水平,利用个人优势,并承认每个成员的弱点。以下是一些将其付诸实践的技巧:

    1. 定义优先级

    如果员工不知道哪些任务是紧急的,哪些不是,他们就无法自我管理,也不能在确定工作优先级时做出明智的决定。试着为每项任务设定最后期限,因为这将为人们提供指导,以了解他们是否在正轨上。

    2. 制定轮班时间表

    朝九晚五的工作时间表早已过时。现在,大多数公司允许员工管理他们的工作时间,提供灵活性,让人们将工作融入他们的生活,而不是相反。

    但这会在团队成员之间造成压力和工作量不平衡。例如,在某个时间没有足够的团队成员工作可能意味着其他人的工作量更大。

    分析公司需求,并在允许灵活性的同时组织人员的日程安排,以确保灵活性不会破坏生产力和团队成员之间的健康平衡。

    3. 让人们了解情况

    尤其是在远程环境中,保持信息可用很重要。请记住,远程工作人员不会随便在咖啡机旁聊天,也不会在走廊里偶遇。积极努力确保每个人都知道他们需要知道什么。

    4. 保持持续和开放的沟通

    在前一点的基础上,确保人们获得他们需要的信息。当他们这样做时,一切都会运行得更快、更顺畅。考虑举行定期会议,人们可以与他们的团队或整个组织共享相关信息。

    另一方面,保持沟通渠道向另一个方向开放。让您的员工觉得他们可以表达自己的感受和意见。这项研究表明,74% 的员工表示,当他们感到被倾听时,他们的工作效率更高。

    通过推行健康的工作量管理,您可以让员工和团队更快乐,让他们更有效率并愿意为公司的成功做出贡献。

    员工如何促进工作与生活的平衡

    公司在员工福利方面发挥着重要作用,但员工也必须发挥自己的作用。美国精神病学协会于 2021 年初对远程工作者进行了一项在线调查。研究结果令人担忧:“大多数在家工作的员工表示,他们经历了负面的心理健康影响,包括孤立、孤独,以及在一天结束后难以离开工作。”

    如果你在远程工作,甚至是混合工作,你需要注意自己的身心健康。在大流行之初广泛传播的建议仍然有效且至关重要,尤其是当您因数月乃至数年的远程工作而感到疲倦时。以下是在家工作时要记住的一些技巧:

    1. 创造一个合适的家庭办公环境

    不要在床上或者沙发上工作。在房子里指定一个特定的空间作为你的家庭办公室,并这样对待它。确保你有一张合适的桌子、一把舒适的椅子和自然光。

    2. 使用质量技术

    随着远程工作的采用,许多公司为员工提供资金来装备他们的家庭工作空间。利用这一点并购买必要的设备,以保证尽可能好的工作空间。如果您的公司不提供支持,那么也许值得与 HR 提出这个话题。

    3. 保持一致的工作时间

    虽然远程工作通常具有灵活工作时间的好处,但请注意其缺点:如果您没有时间表,工作将占据您的一整天。

    要充分利用灵活性来安排一些活动:诸如送孩子上学或去看医生,但不要让其他所有事情都进入您的工作时间,否则您会有一直在值班的感觉。

    4. 吃好睡好

    时刻给予身体适当的休息和营养。在家工作时,很容易养成不健康的习惯,例如熬夜或全天吃零食。小心,因为这些会影响您的工作效率和整体幸福感。

    5. 移动你的身体

    特别是疫情期间,很容易在空闲时间坐在同一张桌子前,看着屏幕完成工作。

    人的身体不是整天坐着的。它是用来移动、感受刺激并与他人互动的。确保您在工作时间休息并有适当的午餐时间,您甚至可以去那里散步。工作完成后,让你的身体动起来。出去,做一些运动,或者尝试瑜伽来伸展你的肌肉,减轻坐姿不良引起的疼痛。

    结论

    GitHub 的 2021 年 Octoverse 状态报告显示,虽然 41% 的受访者在疫情之前曾在同一办公室办公,但预计只有 10.7% 的受访者在疫情结束后会留在办公室。此外,与疫情前相比,完全接受远程工作的公司预计将增加 46%。

    这些趋势表明,数字化转型是生存的关键。然而,70% 的数字化转型计划未能实现其目标。


    【注】本文译自:Software Development Trends for 2022 – DZone Agile趋势