跟ApacheBeam学质量控制之道

简介: 在学习、开发Apache Beam源码过程中,除了它精妙的设计(通过几个简单的概念抽象把实时和离线的计算逻辑模型统一了起来),庞大的代码量(Java 33万行, Python9万行),还有一个比较大的感受是它的质量控制做得特别好,比之前参与过的其它一些开源项目都要好,这可能跟Google的工程质量高于业界有关。但是这里面也没什么什么奇技淫巧,只是善用了一些插件而已,我在这里想把我在Apache B

在学习、开发Apache Beam源码过程中,除了它精妙的设计(通过几个简单的概念抽象把实时和离线的计算逻辑模型统一了起来),庞大的代码量(Java 33万行, Python9万行),还有一个比较大的感受是它的质量控制做得特别好,比之前参与过的其它一些开源项目都要好,这可能跟Google的工程质量高于业界有关。但是这里面也没什么什么奇技淫巧,只是善用了一些插件而已,我在这里想把我在Apache Beam里面看到、学到的一些质量实践分享给大家。代码质量提升其实没有什么太多的捷径,这里要分享的也不是多么高大上的道理,每个小点都是很零碎的一个小技巧,但是所有这些小技巧组合起来,会让你对代码的质量更有信心。

我们代码里面的一些常见问题

事物的好坏是对比出来的,我们先来看看我们代码里面一些问题。

随意的JavaDoc

比如:

/**
 * UserService
 *
 * @author <somebody>
 * @version $Id: UserService.java, v 0.1 2014年8月28日 上午11:04:58 <somebody> Exp $
 */
public interface UserService extends ExtUserService {

再比如:

/**
 * DataProjectService
 *
 * @author <somebody>
 * @since created on 2015-10-28 22:27
 */
public interface DataProjectService {

我们代码很多没有javadoc, 或者好一点的有javadoc,但是不符合javadoc规范(用javadoc实际去生成会报格式不对)。

超长的代码

下面截取一段CAP里面的代码:

    /**
     * // 看这里
     * @see com.alipay.cap.core.service.DataProjectAccessKeyService#updateExcuteAKById(java.lang.Long, java.lang.String, java.lang.String)
     */
    @Override
    public void updateExcuteAKByUnionKey(String namespaceId, String dataProjectKey, String accessId, String accessKey, String operator) {
        CapNamespaceProjectAkCriteria example = new CapNamespaceProjectAkCriteria();
        example.createCriteria()
            .andDataProjectKeyEqualTo(dataProjectKey)
            .andNamespaceIdEqualTo(namespaceId)
            .andIsDeletedEqualTo(false);
        List<CapNamespaceProjectAkDO> capNamespaceProjectAkDOs = capNamespaceProjectAkDAO.selectByExample(example);
        // .....
    }

比如这里这行代码长达138,与一般的推荐配置(80, 最多100)长多了,已经超出一屏了,看起来很难受。

重复的、未使用的maven依赖

用依赖分析工具分析一下CAP的代码依赖,可以发现,里面有很多没用的依赖以及用到了但是没有声明的依赖。

[INFO] --- maven-dependency-plugin:2.8:analyze (default-cli) @ cap-web-openapi ---
[WARNING] Used undeclared dependencies found:
[WARNING]    com.alipay.cap:cap-common-client:jar:1.0.20140703:compile
[WARNING]    com.alipay.cap:cap-common-conf-core:jar:1.0.20170512:compile
[WARNING]    com.alipay.cap:cap-common-dal:jar:1.1:compile
[WARNING]    com.google.code.findbugs:annotations:jar:2.0.3:compile
[WARNING]    com.alibaba.alimonitor:alimonitor-jmonitor:jar:1.0.4:compile
[WARNING]    org.slf4j:slf4j-api:jar:1.7.14:compile
[WARNING]    com.alipay.dpc:smartmw-cache:jar:2.0.20161227:compile
[WARNING]    org.springframework:org.springframework.context:jar:3.0.5.RELEASE:compile
[WARNING]    org.springframework:spring-modules-validation:jar:0.9:compile
[WARNING]    org.springframework:org.springframework.core:jar:3.0.5.RELEASE:compile
[WARNING]    com.alibaba:fastjson:jar:1.2.8.sec01:compile
[WARNING]    com.alibaba.toolkit.common:toolkit-common-lang:jar:1.1.1:compile
[WARNING]    com.alipay.sofa:sofa-runtime-api:jar:4.5.2:compile
[WARNING] Unused declared dependencies found:
[WARNING]    com.alipay.sofa.service:sofa-service-api:jar:3.2.4.1:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-sofa-env-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-alipay-toolbox-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-toolbox-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-validation-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-tair-session-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-widget-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-uisvr-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-json-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-resource-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-alipay-security-plugin:jar:4.1.6:compile
[WARNING]    com.alipay.sofa.web.mvc:mvc-alipay-auth-plugin:jar:4.1.6:compile
[WARNING]    commons-beanutils:commons-beanutils:jar:1.7.0:compile
[WARNING]    com.alipay.cap:cap-biz-service-impl:jar:1.0:compile
[WARNING]    org.springframework:spring-webmvc:jar:3.2.2:compile

这些每个问题单个看起来都不是大问题,但是堆积起来会让代码看起来有点

Beam里面的一些小实践

下面我介绍一下我在Beam里面看到的质量控制相关的一些小技巧,主要是一些maven插件的使用和一些测试库的使用,以及一些思想上的意识。

Dependency插件

Dependency插件的作用是帮我们扫描项目的依赖问题,它可以把我们实际要到了,但是没有声明的依赖;或者实际没用到,但是声明了的依赖都给找出来,帮你保持代码的纯洁, 下面是一个扫描报错的例子:

[INFO] --- maven-dependency-plugin:3.0.1:analyze-only (default) @ beam-dsls-sql ---
[WARNING] Unused declared dependencies found:
[WARNING]    com.google.auto.value:auto-value:jar:1.4.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 27.409 s
[INFO] Finished at: 2017-06-16T11:33:03+08:00
[INFO] Final Memory: 50M/401M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-dependency-plugin:3.0.1:analyze-only (default) on project beam-dsls-sql: Dependency problems found -> [Help 1]
[ERROR]

这里我们声明了一个没有用到的,但是声明了的auto-value的依赖,因此编译失败了。有了这个插件的保证,我们可以很有自信的知道我们引入的每个依赖都是有用的,有意义的。

Checkstyle

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. It automates the process of checking Java code to spare humans of this boring (but important) task. This makes it ideal for projects that want to enforce a coding standard.

它能做的一些典型检查包括:

  • FallThrough: 如果你的switch/case里面没有写break,它会自动检测出来。
  • CustomImportOrder: 检查import的顺序符合指定样式。
  • LineLength: 检查代码行数不要超长。
  • MethodLength: 检查方法行数不要超长。

下面是我最近在写Beam代码的时候编译时候报的几个Checkstyle错误:

[bash] There are 5 errors reported by Checkstyle 6.19 with beam/checkstyle.xml ruleset.
[ERROR] src/main/java/org/apache/beam/dsls/sql/meta/Column.java:[19](javadoc) JavadocType: Missing a Javadoc comment.
[ERROR] src/main/java/org/apache/beam/dsls/sql/meta/DefaultMetaStore.java:[9](javadoc) JavadocParagraph: Empty line should be followed by <p> tag on the next line.
[ERROR] src/main/java/org/apache/beam/dsls/sql/meta/Table.java:[21](javadoc) JavadocType: Missing a Javadoc comment.
[ERROR] src/main/java/org/apache/beam/dsls/sql/meta/Table.java:[35](javadoc) JavadocParagraph: Empty line should be followed by <p> tag on the next line.
[ERROR] src/main/java/org/apache/beam/dsls/sql/rel/BeamValuesRel.java:[60](sizes) LineLength: Line is longer than 100 characters (found 115).
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 27.517 s
[INFO] Finished at: 2017-06-19T14:49:28+08:00
[INFO] Final Memory: 49M/478M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:2.17:check (default) on project beam-dsls-sql: You have 5 Checkstyle violations. -> [Help 1]

Enforcer插件

Enforcer是另一比有意思的maven插件, 它可以帮你指定你代码所需的运行环境,比如需要的maven版本(maven不同版本之间有时候行为差异还是很大的,比如我们采云间的代码基本都是用3.x版本编译的,但是开发机上默认装的是2.x的,这样编译的时候就会报很奇怪的错误,而enforcer插件则可以把这个需求明确化,如果你的maven的版本不对,它会明确告诉你maven版本不对,而不是其它的诡异错误)。它能做到的一些检查包括:

  • Maven的版本
  • JDK的版本
  • OS的版本
  • 检查指定的属性(property)是否存在

在我看来maven-enforcer-plugin有点像java语言里面assert,assert如果失败了,说明运行环境有问题,直接失败。

下面是一个Maven版本不对的错误:

[INFO] --- maven-enforcer-plugin:1.4.1:enforce (enforce) @ beam-dsls-sql ---
[WARNING] Rule 2: org.apache.maven.plugins.enforcer.RequireMavenVersion failed with message:
Detected Maven Version: 3.1.1 is not in the allowed range [3.2,).

Google AutoValue

最近这些年函数式编程的思维开始流行起来,很多非函数式的语言也开始在语言层面支持某些函数式的特征,比如Java 8里面的lambda, stream等等,Google AutoValue也是类似的目的,它的目的是让你的POJO编程readonly的(只有Getter), 从而实现函数式语言里面Immutable的特性。

它最大的贡献在于,它让你只需要定义你需要的字段:

import com.google.auto.value.AutoValue;

@AutoValue
abstract class Animal {
  static Animal create(String name, int numberOfLegs) {
    // See "How do I...?" below for nested classes.
    return new AutoValue_Animal(name, numberOfLegs);
  }

  abstract String name();
  abstract int numberOfLegs();
}

这里我们定义了我们需要两个字段: namenumberOfLegs以及一个用来构建Animal对象的create方法,其它的则都由AutoValue自动生成,其中AutoValue_Animal就是AutoValue自动生成的类。在AutoValue_Animal里面,它帮我们自动实现了hashCode, equals, toString等等这些重要的方法。这些方法的特征是实现的过程基本都一样,但是容易出错(由于粗心), 那么不如交给框架去自动产生。

如果你的类字段比较多,那么AutoValue还支持Builder模式:

import com.google.auto.value.AutoValue;

@AutoValue
abstract class Animal {
  abstract String name();
  abstract int numberOfLegs();

  static Builder builder() {
    return new AutoValue_Animal.Builder();
  }

  @AutoValue.Builder
  abstract static class Builder {
    abstract Builder setName(String value);
    abstract Builder setNumberOfLegs(int value);
    abstract Animal build();
  }
}

这样我们就可以一步一步渐进地把对象构造出来了。

Api Surface Test

所谓的API Surface是指我们一个系统暴露给另外一个系统一个SDK的时候,我们到底应该把哪些类,哪些package暴露给用户,这个问题很重要,因为考虑向后兼容性的话,暴露的package越少,将来修改的时候,破坏向后兼容性的可能性就越小,SDK就越稳定。我们平常的时候对于这种事情可能都是通过人肉分析、review,在Beam里面它直接编写成了一个单元测试:

  @Test
  public void testSdkApiSurface() throws Exception {

    @SuppressWarnings("unchecked")
    final Set<String> allowed =
        ImmutableSet.of(
            "org.apache.beam",
            "com.fasterxml.jackson.annotation",
            "com.fasterxml.jackson.core",
            "com.fasterxml.jackson.databind",
            "org.apache.avro",
            "org.hamcrest",
            // via DataflowMatchers
            "org.codehaus.jackson",
            // via Avro
            "org.joda.time",
            "org.junit");

    assertThat(
        ApiSurface.getSdkApiSurface(getClass().getClassLoader()), containsOnlyPackages(allowed));
  }

这个测试表明,Beam的SDK向外暴露的package就是上面列出的这些,下次来个新手贡献的代码如果破坏了这个约定,那么这个单元测试就直接报错,无法提交merge。还是那句话,凡是能自动化检测的东西不要依靠人肉。这里涉及到的主要技术是:

  1. 通过扫描classpath对指定包里面所有类的方法,参数,返回值进行检查。
  2. hamcrest这个支持matcher的单元测试的库,它让你不只可以assertTrue, assertFalse, assertEquals, 而是可以自由指定需要满足的条件。

总结

给Beam贡献代码的时候最大感受在于,你不需要去问其它Commiter或者看什么文档去确认你的代码风格是否符合,是否用对了Maven版本,JDK的版本对不对,JavaDoc需不需要写等等,你只要编译下代码,maven会告诉你的代码是否OK。只要通过了编译,代码风格、一些小的错误等等基本不会有了,那么代码review的时候主要就集中在代码设计层面了。有用的maven插件还有很多,这里只是举了几个例子,我觉得比每个插件本身更重要的是:

  1. 对代码质量的重视的意识。
  2. 使用工具/代码(而不是文档、流程)来解决保障代码质量。
目录
相关文章
|
19天前
|
测试技术 持续交付 UED
软件测试的艺术:确保质量的实战策略
在软件开发的舞台上,测试是那把确保每个功能如交响乐般和谐奏响的指挥棒。本文将深入探讨软件测试的重要性、基本类型以及如何设计高效的测试策略。我们将通过一个实际的代码示例,展示如何运用这些策略来提升软件质量和用户体验。
|
2月前
|
机器学习/深度学习 人工智能 监控
提升软件质量的关键路径:高效测试策略与实践在软件开发的宇宙中,每一行代码都如同星辰般璀璨,而将这些星辰编织成星系的过程,则依赖于严谨而高效的测试策略。本文将引领读者探索软件测试的奥秘,揭示如何通过精心设计的测试方案,不仅提升软件的性能与稳定性,还能加速产品上市的步伐,最终实现质量与效率的双重飞跃。
在软件工程的浩瀚星海中,测试不仅是发现缺陷的放大镜,更是保障软件质量的坚固防线。本文旨在探讨一种高效且创新的软件测试策略框架,它融合了传统方法的精髓与现代技术的突破,旨在为软件开发团队提供一套系统化、可执行性强的测试指引。我们将从测试规划的起点出发,沿着测试设计、执行、反馈再到持续优化的轨迹,逐步展开论述。每一步都强调实用性与前瞻性相结合,确保测试活动能够紧跟软件开发的步伐,及时适应变化,有效应对各种挑战。
|
3月前
|
敏捷开发 安全 测试技术
软件测试的艺术:确保质量与性能的平衡之道
【9月更文挑战第24天】在软件开发的海洋中,测试是导航灯塔,指引着项目安全抵达质量的彼岸。本文将深入探讨软件测试的核心原则、方法论以及如何通过精心设计的测试策略来保障产品的可靠性和性能。我们将从测试的基础知识出发,逐步深入到高级测试技巧,最终展示如何通过实际案例来应用这些知识以确保软件的成功交付。
|
3月前
|
测试技术 持续交付 UED
软件测试的艺术与科学:平衡创新与质量的探索在软件开发的波澜壮阔中,软件测试如同灯塔,指引着产品质量的方向。本文旨在深入探讨软件测试的核心价值,通过分析其在现代软件工程中的应用,揭示其背后的艺术性与科学性,并探讨如何在追求技术创新的同时确保产品的高质量标准。
软件测试不仅仅是技术活动,它融合了创造力和方法论,是软件开发过程中不可或缺的一环。本文首先概述了软件测试的重要性及其在项目生命周期中的角色,随后详细讨论了测试用例设计的创新方法、自动化测试的策略与挑战,以及如何通过持续集成/持续部署(CI/CD)流程优化产品质量。最后,文章强调了团队间沟通在确保测试有效性中的关键作用,并通过案例分析展示了这些原则在实践中的应用。
84 1
|
3月前
|
测试技术 Python
软件测试的艺术:确保质量与性能
【9月更文挑战第19天】在数字化时代,软件已成为我们生活的一部分。然而,随着软件复杂性的增加,如何确保其质量和性能成为了一个挑战。本文将探讨软件测试的重要性,介绍常见的测试类型和策略,并提供实用的代码示例来帮助读者更好地理解和应用这些测试方法。无论你是开发人员、测试工程师还是项目管理者,这篇文章都将为你提供有价值的见解和技巧。
|
3月前
|
测试技术 UED 开发者
软件测试的艺术:从代码审查到用户反馈的全景探索在软件开发的宇宙中,测试是那颗确保星系正常运转的暗物质。它或许不总是站在聚光灯下,但无疑是支撑整个系统稳定性与可靠性的基石。《软件测试的艺术:从代码审查到用户反馈的全景探索》一文,旨在揭开软件测试这一神秘面纱,通过深入浅出的方式,引领读者穿梭于测试的各个环节,从细微处着眼,至宏观视角俯瞰,全方位解析如何打造无懈可击的软件产品。
本文以“软件测试的艺术”为核心,创新性地将技术深度与通俗易懂的语言风格相结合,绘制了一幅从代码审查到用户反馈全过程的测试蓝图。不同于常规摘要的枯燥概述,这里更像是一段旅程的预告片,承诺带领读者经历一场从微观世界到宏观视野的探索之旅,揭示每一个测试环节背后的哲学与实践智慧,让即便是非专业人士也能领略到软件测试的魅力所在,并从中获取实用的启示。
|
4月前
|
监控 测试技术 UED
软件测试的艺术:确保质量的五个关键实践
【8月更文挑战第28天】 在软件开发领域,测试不仅是发现错误的工具,更是确保产品稳定性、性能和用户满意度的基石。本文将深入探讨五个关键的软件测试实践,包括单元测试、集成测试、系统测试、压力测试和回归测试,以及它们如何共同构建起一个坚实的质量保证体系。通过这些实践,我们不仅能够提升软件的质量,还能更好地预测和应对潜在的风险,从而为最终用户提供更加稳定可靠的软件产品。
59 5
|
4月前
|
测试技术 持续交付 开发者
持续部署的内涵和实施路径问题之质量内建对持续部署有何重要性
持续部署的内涵和实施路径问题之质量内建对持续部署有何重要性
|
4月前
质量标准化实践问题之功能预演中出现问题如何解决
质量标准化实践问题之功能预演中出现问题如何解决
25 1
|
4月前
|
测试技术 持续交付
软件测试的艺术:确保质量的探索之旅
在数字化时代的浪潮中,软件成为我们生活和工作不可或缺的一部分。但在这背后,隐藏着一个不为人知的世界——软件测试。它如同一位默默无闻的守护者,确保每一个程序的稳定性、可靠性与性能。本文将带您走进软件测试的核心,揭示它的重要性,探索不同的测试方法,并分享如何通过持续集成和自动化测试提升效率。让我们一同踏上这场确保软件质量的探索之旅,发现那些让软件变得更加完美的秘诀吧!