当法律遭遇人工智能

在当今的法律体系中,法律条文的制定往往跟不上时代前进的脚步,落后的条文(或者判例)有时反而会成为新生事物的制约;部分领域的法律条文或者判例卷帙浩繁,即使是专业的法律人也得耗费数年的学习和实践才能慢慢掌握法条的场景和使用;而即便是这么多的法律条文也难以将现实发生的所有复杂情况一一枚举,因此根据案件的实际情况参考哪些法条和如何定罪与量刑是法官、律师、陪审团等法律程序参与者的任务。这种人工干预有巨大操作和斡旋空间,也就出现了各种州级法院和联邦法院判断不一致的情况—— 明明案件内容是明确的,法律条文是明确的,然而判断却是不清晰的。

这也引发了我几年前的一个脑洞:既然现实情况如此复杂,法律条文迭代缓慢且难以穷尽,为什么我们不能让机器代替我们去学习历史上那些庞大的案件判例和法条规则,然后对每一个新的法律案件或纠纷做一个定罪的多分类,这样产生的”机器判官”,其判案准确率或许要比法官要准确的多。无论法官多么经验丰富,在面对一个新案件时,其援引的判例也是有限的,而一个训练好的模型或许能将历史上所有相关的案件都考虑进来。

我以为我有了一个好主意,兴冲冲地去网上搜索,才发现 AI 和法律领域的结合早在1987年就有人开始做了,而且他们几十年来不断尝试的领域和方法也是我未曾料想到的。然而,他们也经历了一个艰辛的过程。

历史

说到 AI and Law (以下简称 AIL), 就不能不提 ICAIL (International Conference on Artificial Intelligence & Law),这是国际唯一一个关注于 AI and Law 领域的会议,自1987年第一举办以来已有31个年头。一部 ICAIL 的历史,其实就是整个 AIL 领域的发展史。

(图 来源[17]:随着时代变迁,ICAIL会议主题云图的变化,从中可以看出 expert system, knowledge base 等的淡出)

对法律条文、案件建模的尝试,早在上个世纪八十年代就开始了。那个时候就已经诞生了基于逻辑的形式化方法进行法律推理 (legal reasoning) 以及人工神经网络的数值方法。还有一些借着专家系统的东风, 通过一些 基于法律条文(rule-based),基于案例(case-based) 和 基于知识(knowledge base) 的手段帮助法律从业者进行辅助推断以及其他工作,这其中比较著名的案例是 HYPO, 一个基于案例的法律推理系统,它几乎引领了基于案例的法律推理潮流。

随着 AI 领域本身的发展,近年来基于专家系统的方法日趋势微,基于统计机器学习的方法则开始大行其道。 比如使用贝叶斯网络等图模型进行证据推理(evidential reasoning);在 E-Discovery 使用 SVM 等方法进行法律文档分类;以及与NLP的结合,如法律领域限定的命名实体识别 (NER), 信息抽取与信息检索等等;基于逻辑的形式化方法也并没有完全消散下去,而是继续在诸如计算法律(computational law) 等子领域贡献着光热。

已有工作

目前围绕 AI and Law 的工作,从功能上大体上可以分成两类: 一类尝试直接对法律本身、案件、推断过程建模,涉及到诸如道义逻辑(Deontic Logic), 非单调逻辑(non-monotonic logic) 等传统基于逻辑的形式化方法,也有基于神经网络,贝叶斯网络等的统计学习方法;另一类则选择了绕行,既然前者困难重重,那么我们不妨退而求其次,利用专家系统、自然语言系统等手段, 通过法律文档化、文献文书检索、实体识别等方法来提高法律人的工作效率,让他们从繁重的重复性工作中解脱出来从而聚焦到更有意义的事情上去,这同样是一件有价值的工作。下面简单地从几个方向分别举例说明。

Reasoning (Rule based Reasoning vs Case based Reasoning)

顾名思义,该领域主要研究法律推理的各种方法。从派系来分,可以分成基于法律规则的推理(rule based reasoning) 和基于案例的推理(case based reasoning)两派。如果说 基于法律规则的推理是将已有规则应用于案件进行推断的话,那么基于案例的推理更侧重于寻找已有比较类似的案件来进行归纳推理。从方法上来说,两者都曾经偏好使用基于逻辑的方法来进行形式化。相关逻辑理论的讨论也是领域焦点。比如考虑齐硕姆悖论(Chisholm paradox),即道义逻辑对法律形式化是否是真正可用的等等。近来基于案例的推理更加偏向主流,其原因可能是支撑基于规则推理的一些传统方法(比如专家系统, knowledge base)渐渐淡出视野,也可能是后者与统计学习的天然联系,毕竟基于案例的推理就是靠对以往大量判例的分析进行推断。

e-discovery

全称 Electronic Discovery。其目标在于为法律诉讼,政府调查等领域提供专门的信息化服务。与传统的纸质信息提供服务不同,E-Discovery 比较注重信息检索的“结构化”,也就是 metadata。比如一个信息条目的时间、地点、涉及领域,甚至事件的形式化描述等。这些信息可以大大优化相关人员的查找效率,节约成本。

由于 eDiscorvery 的本质就是从基于纯文本的文档中提取有价值的结构信息用于从业者的检索和查阅分析; 因此从解决方案上目前基本上与 NLP 联系较紧,比如之前提到的命名实体识别、自动生成文摘、情感分类等。通过这些方法,可以从法律文本中自动提取信息,自动化生成相关专业领域的文件,推动法律文档化等。

computational law

计算法律可以说几乎是与法律人工智能的最终态—— AI判官最为接近的领域了, 其目标是法律本身以及案件的形式化表达和自动推理。

The Computational Law project focusses on formalization of governmental regulations and enterprise policies, development of automated reasoning procedures for compliance checking, legal planning and regulatory analysis, and developing user-facing computer systems

该领域涉及到的方法暂时还主要偏形式化( Computational Logic ),分两部分:第一部分是事实和规则的形式化表达;第二部分是使用一些机械推理技术来分析案件自动产生推断结果。目前计算法律的发展暂时还没有成熟到可以完全使用的程度,原因是逻辑方法很多都是基于规则的,很难 case-by-case 地对复杂判例做去完全表达,这对于使用英美法系的国家来说尤为严重。

当代:与 NLP 的结合

目前在 AIL 领域应用的最多的还是自然语言处理等相关技术。因为无论是对法律本身建模还是建立一些帮助法律从业者的辅助系统,都需要我们从现有的法律条文,案件描述卷宗等资料出发,将其信息化和结构化,这是必须的第一步,而解决该问题的方法还需从 NLP 入手。比如我们可以根据主题模型(Topic Model) 将案件通过涉及到不同的法律法规或者量刑的不同进行聚类;通过信息抽取来生成案件的结构化描述信息;通过自动文摘加快从业者浏览文件的效率;通过QA来建立法律领域的自动问答系统来尝试在线法院等等。这也与当下推动“智慧法院”,将法院数字化,自动化的主旨相符。下面举出几个例子。

1 EBravia

EBravia 是一家致力于法律文档结构化的公司。它通过先端的机器学习技术,可以从非结构化的原始文本中提取里面蕴含的时间,地点,以及法律领域相关的关键词,短语,句子。大幅度节约法律工作者从文本里人工挖掘信息的时间。

(图:来自EBrevia 官网[19] 的视频简介)

2 Legal Robot

这家名为 Legal Robot 的公司则更侧重法律文档审校和合同文本分析。它推出的合同文本分析工具不仅能帮助相关工作者校对领域文法的错误;还能基于海量的合同样本数据,生成一些常用的领域短语和句子, 帮助人写出更加符合领域需要的法律文书。同时,对于已有的合同文书,它还能捕捉到里面表示模糊,给当事人带来隐性法务风险的词句,从而在风险管理领域发挥强大的作用。

(Legal Robot 样例, 来源[20])

3 LUIMA

Luima, 即 Legal UIMA, 是一个由 IAAIL 及 CMU 等相关领域的研究员共同研发的法律检索系统。它专注在疫苗伤害事故相关的法律文本,对用户搜索的 query 给出包含对应法律条文或者类似案件信息的文档用于参考和对比分析。具体而言,Luima 系统整体上分成四个模块:法律语义标注,文档存储与索引,文档搜索,搜索结果排序。

(图: 来自[13] LUIMA,一个 法律文档检索系统的 Pipeline)

对一个法律文档,Luima 会先将其拆成句子级的文本。对每个句子做 Term, Mention, Formulation 三个级别的标注,然后通过这三中标注将每个句子分为 LegalRuleSentence, EvidenceBasedFindingSentence 和 other 三个分类。对于该分类任务,Liuma 使用了句子的 n-gram TF-IDF 和上述的法律语义标注信息做特征,使用 朴素贝叶斯,逻辑回归,决策树等方法训练出一个分类模型。这样输入的原始文档就会转化成多个带有语义标注即句子级分类标签的句子集合,并存入数据库中。当用户执行查询操作的时候,Luima 会先通过 Lucence 引擎找出 Top-30 个文档结果作为预选的“专家文档”,然后对这30篇文档的 candidates 做二次重排进而筛选出更符合条件的搜索结果。进行二次重排是为了避免那些“仅仅文档的文本相似但是实际语义毫不相关”的情况。该过程综合考虑了之前产生的相关法律语义匹配情况以及文本相似度等多个特征,使用逻辑回归对文档排名进行打分,返回最终的文档结果给用户。

从 Luima 我们可以看到基于自然语言处理的技术(TF-IDF 等特征),机器学习模型(逻辑回归等)是如何与法律专业知识(领域级语义标注)结合起来并发挥作用的。

除了这三者之外,还有一些通过机器学习来做法律推理的方法,不过目前还是停留在 Paper 级别的居多,就不加赘述了。

问题

当然,AIL 的发展也并非前景大好。有一些内部本身以及涉及到跨行业的矛盾仍未得到解决。

法理学的角度

从笔者对法理学最浅薄的理解,一切法律都是依据所处国家,所处文化,道德导向等种种因素的基础上,对“自然法”——这一正义的基本和终极的原则的集合的诠释。而随着法律人工智能的进行,无论是逻辑还是模型,是否真的能体现法理学精神;是否能被传统法律从业者所接受;法律行业与人工智能领域这两个职业的最佳契合点到底在哪里,这些问题仍待解决。“人工智能能做哪些事,不能做哪些事,机器判官是否能为人所接受”;其包含的技术风险和伦理风险,可能不再是公众号和媒体的纸上谈兵。

法律过程与事实抽象的困难

法律不是数学规律,她不仅仅包含那一个个孤零零的发条。一些与现实紧密结合的法律过程和事实,比如陪审团,比如对抗式刑事审判,这些实际过程如何进行数学或者模型的抽象,都不是一个好解决的问题。

对AI本身发展的巨大依赖

从 ICAIL 的历史可以看出, AIL 的发展,几乎是强依赖与AI本身的发展的。90年代前 knowledge base 以及专家系统是主流的时候,大量基于它们的论文也如同雨后春笋,而现在则门可罗雀,统计学习和 NLP 则甚嚣尘上。这不由得不令人怀疑 AIL 是否仅仅只是 AI 的一个附庸。 到底 AIL 能不能独立出一套与其他领域无关的方法论,然后将其他领域的方法接纳进来为我所用,也可能是一个今后需要解决的问题。

展望

AIL 的理论在上个世纪得到了巨大发展,而具体的落地则刚刚开始。从“智慧法庭”和法律文档化的潮流上看,AIL 至少是一个到处都需要填坑的朝阳产业。但是计算法律的理论,AI判官是否还能继续前进下去,ICAIL 能否不再是一个两年一届的C类会而真正走向 AI 的大家庭之中,就看之后的发展了。

(图: Research – Practise Paradox, 来源[17])

Reference

  1. codex
  2. History: 50 best papers of ICAIL
  3. Connectionist expert system
  4. computational law
  5. HYPO: A Process Model of Legal Argument with Hypotheticals
  6. NLP-E-discovery
  7. conceptual retrieval and case law
  8. Formalizing Arguments, Rules and Cases
  9. Law and Logic: a Review from an Argumentation Perspective
  10. Deontic Logic
  11. Constructing and Understanding Bayesian Networks for Legal Evidence with Scenario Schemes
  12. NEST: A Compositional Approach to Rule-Based and Case-Based Reasoning
  13. LUIMA: Introducing LUIMA: an experiment in legal conceptual retrieval of vaccine injury decisions using a UIMA type system and tools
  14. 法律智能十大趋势
  15. AI and Law, 人工智能与法律(一)
  16. Law Department Artificial Intelligence Survey Report
  17. 25 Years of AI & Law ICAIL 1987 – 2013
  18. AI in Law and Legal Practice
  19. EBrevia
  20. Legal Robot
  21. 美国最高法院网上新判例
Share

Service mesh 服务网格 | 雷达哔哔哔

位置

2018年11月第19期技术雷达,技术象限,建议评估。(最新版技术雷达已经发布,点击这里下载

标签

Microservices,linkerd,Istio

目标受众

系统架构师、开发人员

关注问题

目前的微服务架构大多基于类似于Spring Cloud全家桶的框架构建,尽管这样可以基本满足构建微服务系统架构在技术上的一些基础需求,例如常见的服务发现、配置管理、熔断、跟踪,安全等。但是也同样也带来了一些限制和成本,例如对于代码的侵入性较强、编程语言绑定、学习成本高等。

解决方案

将解决分布式架构下安全、熔断、限流、降级、服务发现、调用链分布式跟踪等功能从业务服务中彻底分离,打包放到Sidecar(边车)中,并挂接到服务上,实现业务逻辑部分与微服务技术架构部分的完全解耦。

解读

近几年微服务热度不减,而构建微服务架构一般要解决两个问题,一个是业务服务划分的问题,一个就是微服务基础技术架构的问题。

在技术架构层面,目前业界可选的通用方案也不多。在Java阵营目前相对主流的方案就是基于Spring Boot+Spring Cloud+Kubernetes来构建微服务基础架构,并辅以ELK,Zipkin,Swagger,Prometheus,Grafana等工具做一些运维监控的工作。

这样虽然可以满足我们在微服务架构(本质上就是一个松耦合的分布式架构)上的一些技术要求,但是也同样也带来了一些新的问题,例如上文提到的代码侵入性强,耦合高,开源框架拼接导致的技术学习成本高,协调配合需要打磨等。

Service Mesh(服务网格)的产生就是为了解决这个问题,而遵循的还是软件行业那句古老的谚语:

“任何软件工程遇到的问题都可以通过增加一个中间层来解决”

Service Mesh就是添加了这么一个中间层,并给他起了个形象的名字:Sidecar。

图片来源于架构之路微信公众号,请参见延展阅读

区分出了这个Sidecar(边车),我们的服务就将精力更多的专注于自身的业务本身。而微服务架构下服务间通讯涉及的所有脏活儿、累活儿就都交给Sidecar去处理。Sidecar和服务是松耦合的,可以挂接上去,也可以分离开。只要接口匹配,对于业务服务完全无侵入,无语言绑定。

Sidecar就像一个一个“助理”一样兢兢业业,而服务则享受老板的待遇,什么事只需要跟Sidecar交代一下,其他就不用管了,而Sidecar也只能通过其他服务的Sidecar与服务交互。 Sidecar之间相互通信,就形成了一张“网格”,这也是服务网格名字的寓意。

图片来源于架构之路微信公众号,请参见延展阅读

是的,Service Mesh添加的这一新的层次,就是我们一直在苦苦追寻的“微服务基础设施层”。这层的沉淀和浮现,让程序员的关注层次又上升了一个抽象,离业务也更近了一步。

顺手抛出个观点:软件开发的演进就是我们程序员所写的程序逐渐靠近业务本身的过程。

其他该抽象的抽象,该沉淀的沉淀:包括操作系统,编译器,开发语言,声明式编程,DSL,各种框架工具,ServiceMesh都是在做这些非业务部分的抽象和沉淀而已。

那再下一步会是什么?按照我上面的观点,目前来看一个很有竞争力的选手就是Serverless architecture,以后我们有机会再聊:)

相关Blip

延展阅读

支持工具

Share

如何增强Linux内核中的访问控制安全

背景

前段时间,我们的项目组在帮客户解决一些操作系统安全领域的问题,涉及到windows,Linux,macOS三大操作系统平台。无论什么操作系统,本质上都是一个软件,任何软件在一开始设计的时候,都不能百分之百的满足人们的需求,所以操作系统也是一样,为了尽可能的满足人们需求,不得不提供一些供人们定制操作系统的机制。当然除了官方提供的一些机制,也有一些黑魔法,这些黑魔法不被推荐使用,但是有时候面对具体的业务场景,可以作为一个参考的思路。

Linux中常见的拦截过滤

本文着重介绍Linux平台上常见的拦截:

  1. 用户态动态库拦截。
  2. 内核态系统调用拦截。
  3. 堆栈式文件系统拦截。
  4. inline hook拦截。
  5. LSM(Linux Security Modules)

动态库劫持

Linux上的动态库劫持主要是基于LD_ PRELOAD环境变量,这个环境变量的主要作用是改变动态库的加载顺序,让用户有选择的载入不同动态库中的相同函数。但是使用不当就会引起严重的安全问题,我们可以通过它在主程序和动态连接库中加载别的动态函数,这就给我们提供了一个机会,向别人的程序注入恶意的代码。

假设有以下用户名密码验证的函数:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char passwd[] = "password";
if (argc < 2) {
printf("Invalid argc!\n");
return;
}
if (!strcmp(passwd, argv[1])) {
printf("Correct Password!\n");
return;
}
printf("Invalid Password!\n");
}

我们再写一段hookStrcmp的程序,让这个比较永远正确。

#include <stdio.h>
int strcmp(const char *s1, const char *s2)
{
/* 永远返回0,表示两个字符串相等 */
return 0;
}

依次执行以下命令,就会使我们的hook程序先执行。

gcc -Wall -fPIC -shared -o hookStrcmp.so hookStrcmp.c
export LD_PRELOAD=”./hookStrcmp.so”

结果会发现,我们自己写的strcmp函数优先被调用了。这是一个最简单的劫持 ,但是如果劫持了类似于geteuid/getuid/getgid,让其返回0,就相当于暴露了root权限。所以为了安全起见,一般将LD_ PRELOAD环境变量禁用掉。

Linux系统调用劫持

最近发现在4.4.0的内核中有513多个系统调用(很多都没用过),系统调用劫持的目的是改变系统中原有的系统调用,用我们自己的程序替换原有的系统调用。Linux内核中所有的系统调用都是放在一个叫做sys_ call _table的内核数组中,数组的值就表示这个系统调用服务程序的入口地址。整个系统调用的流程如下:

当用户态发起一个系统调用时,会通过80软中断进入到syscall hander,进而进入全局的系统调用表sys_ call _table去查找具体的系统调用,那么如果我们将这个数组中的地址改成我们自己的程序地址,就可以实现系统调用劫持。但是内核为了安全,对这种操作做了一些限制:

  1. sys_ call _table的符号没有导出,不能直接获取。
  2. sys_ call _table所在的内存页是只读属性的,无法直接进行修改。

对于以上两个问题,解决方案如下(方法不止一种):

  1. 获取sys call table的地址 :grep sys _ call _table /boot/System.map-uname -r
  2. 控制页表只读属性是由CR0寄存器的WP位控制的,只要将这个位清零就可以对只读页表进行修改。
/* make the page writable */
int make_rw(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);//查找虚拟地址所在的页表地址
pte->pte |= _PAGE_RW;//设置页表读写属性
return 0;
}
/* make the page write protected */
int make_ro(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte &= ~_PAGE_RW;//设置只读属性
return 0;
}

开始替换系统调用

本文实现的是对 ls这个命令对应的系统调用,系统调用号是 _ NR _getdents。

static int syscall_init_module(void)
{
orig_getdents = sys_call_table[__NR_getdents];
make_rw((unsigned long)sys_call_table); //修改页属性
sys_call_table[__NR_getdents] = (unsigned long *)hacked_getdents; //设置新的系统调用地址
make_ro((unsigned long)sys_call_table);
return 0;
}

恢复原状

static void syscall_cleanup_module(void)
{
printk(KERN_ALERT "Module syscall unloaded.\n");
make_rw((unsigned long)sys_call_table);
sys_call_table[__NR_getdents] = (unsigned long *)orig_getdents;
make_ro((unsigned long)sys_call_table);
}

使用Makefile编译,insmod插入内核模块后,再执行ls时,就会进入到我们的系统调用,我们可以在hook代码中删掉某些文件,ls就不会显示这些文件,但是这些文件还是存在的。

堆栈式文件系统

Linux通过vfs虚拟文件系统来统一抽象具体的磁盘文件系统,从上到下的IO栈形成了一个堆栈式。通过对内核源码的分析,以一次读操作为例,从上到下所执行的流程如下:

内核中采用了很多c语言形式的面向对象,也就是函数指针的形式,例如read是vfs提供用户的接口,具体底下调用的是ext2的read操作。我们只要实现VFS提供的各种接口,就可以实现一个堆栈式文件系统。Linux内核中已经集成了一些堆栈式文件系统,例如Ubuntu在安装时会提醒你是否需要加密home目录,其实就是一个堆栈式的加密文件系统(eCryptfs),原理如下:

实现了一个堆栈式文件系统,相当于所有的读写操作都会进入到我们的文件系统,可以拿到所有的数据,就可以进行做一些拦截过滤。

以下是我实现的一个最简单的堆栈式文件系统,实现了最简单的打开、读写文件,麻雀虽小但五脏俱全。

https://github.com/wangzhangjun/wzjfs

inline hook

我们知道内核中的函数不可能把所有功能都在这个函数中全部实现,它必定要调用它的下层函数。如果这个下层函数可以得到我们想要的过滤信息内容,就可以把下层函数在上层函数中的offset替换成新的函数的offset,这样上层函数调用下层函数时,就会跳到新的函数中,在新的函数中做过滤和劫持内容的工作。所以从原理上来说,inline hook可以想hook哪里就hook哪里。

inline hook 有两个重要的问题:

  1. 如何定位hook点。
  2. 如何注入hook函数入口。

对于第一个问题:

需要有一点的内核源码经验,比如说对于read操作,源码如下:

在这里当发起read系统调用后,就会进入到sys read,在sys read中会调用vfs read函数,在vfs read的参数中正好有我们需要过滤的信息,那么就可以把vfs_ read当做一个hook点。

对于第二个问题:

如何Hook?这里介绍两种方式:

第一种方式:直接进行二进制替换,将call指令的操作数替换为hook函数的地址。

第二种方式:Linux内核提供的kprobes机制。

其原理是在hook点注入int 3(x86)的机器码,让cpu运行到这里的时候会触发sig trap信号,然后将用户自定义的hook函数注入到sig trap的回调函数中,达到触发hook函数的目的。这个其实也是调试器的原理。

LSM

LSM是Linux Secrity Module的简称,即linux安全模块。是一种通用的Linux安全框架,具有效率高,简单易用等特点。原理如下:

LSM在内核中做了以下工作:

  1. 在特定的内核数据结构中加入安全域。
  2. 在内核源代码中不同的关键点插入对安全钩子函数的调用。
  3. 加入一个通用的安全系统调用。
  4. 提供了函数允许内核模块注册为安全模块或者注销。
  5. 将capabilities逻辑的大部分移植为一个可选的安全模块,具有可扩展性。

适用场景

对于以上几种Hook方式,有其不同的应用场景。

  1. 动态库劫持不太完全,劫持的信息有可能满足不了我们的需求,还有可能别人在你之前劫持了,一旦禁用LD_ PRELOAD就失效了。
  2. 系统调用劫持,劫持的信息有可能满足不了我们的需求,例如不能获取struct file结构体,不能获取文件的绝对路径等。
  3. 堆栈式文件系统,依赖于Mount,可能需要重启系统。
  4. inline hook,灵活性高,随意Hook,即时生效无需重启,但是在不同内核版本之间通用性差,一旦某些函数发生了变化,Hook失效。
  5. LSM,在早期的内核中,只能允许一个LSM内核模块加载,例如加载了SELinux,就不能加载其他的LSM模块,在最新的内核版本中不存在这个问题。

总结

篇幅有限,本文只是介绍了Linux上的拦截技术,后续有机会可以一起探讨windows和macOS上的拦截技术。事实上类似的审计HOOK放到任何一个系统中都是刚需,不只是kernel,我们可以看到越来越多的vm和runtime甚至包括很多web组件、前端应用都提供了更灵活的hook方式,这是透明化和实时性两个安全大趋势下最常见的解决方案。


更多精彩洞见,请关注微信公众号:思特沃克

Share

DDD该如何学?

2006年,国内互联网才刚刚萌芽,大家甚至还不习惯网购,大多数在校生都在宿舍里刷魔兽世界副本。但企业软件开发却得到了蓬勃发展,各大公司和事业单位都纷纷进行信息化转型。

然而大家很快发现,企业应用业务逻辑的复杂度要远远高于技术本身,且企业IT人员很难描述清楚他们真正的业务,广大程序员也普遍缺乏挖掘真正需求的能力。整个开发过程更多的是瀑布式,开发人员一次性收集需求,可能半年后才会和业务人员再次沟通。大多数企业软件就是在这样的环境下硬着头皮上线的,其质量可想而知。

随着《领域驱动设计》中文版的首次发布,DDD(Domain-Driven Design,领域驱动设计)的概念正式进入中国。当时业界普大喜奔,认为它能指导程序员更精准地收集领域知识,进行更合理的设计,企业应用的银弹出现了。

当时的我正处于多层架构的启蒙阶段,挣扎于企业系统的泥潭,又刚刚被Martin Fowler的《企业应用架构》洗了一遍脑,自然也随波逐流地买了一本,捧在手里翻来翻去,但反反复复就是看不懂。当时以为在贫血模型里面加几个方法就是领域模型了,把DAL或DAO改名成Repository就是资源库了。而且身为程序员,自然愿意去关注那些能指导我们写代码的战术设计方法,对那些真正能帮助我们进行合理设计的战略设计方法,则视而不见(可能是因为看也看不懂)。

多年过去,这本书仍然作为我的镇宅之宝戳在书架显眼的位置,希望能有识货的朋友来访时能一眼瞧见,伸出大拇指羡慕嫉妒地说“这么老的书你都有”。或者偶尔拿出来拍张照片在朋友圈晒晒,以炫耀自己当初的见识。顺便翻开一页,把鼻子凑上去闻一闻来自12年前的墨香。

7年之后Vaughn Vernon出版了Implement Domain-Driven Design,简称IDDD。一年之后由同事翻译的中文版《实现领域驱动设计》也相应出版,当时被看做是能让DDD落地的书(毕竟书名里有个“实现”嘛)。然而我在项目技术负责人的带领下,在众多有经验的架构师的指导下,仍然没有弄明白。之前看过的相关知识均已遗忘殆尽。限界上下文、上下文映射这些名词只是似曾相识。

两年之后《领域驱动设计模式、原理与实践》问世,简称PPPDDD。社区对这本书的评价非常之高,甚至认为在IDDD之上。只可惜这本书的翻译质量并不高,我翻了几页之后又束之高阁了。

今年年初,项目上的架构小组又开始组织学习DDD。所使用的“教材”是英文版的PPPDDD。在同事的激励下,我开始重整旗鼓,啃这本英文版大部头。开始精读之后,才发现这是一本很水的好书。说它水是因为它的编排并不足够细心,甚至有不同章节的两段文字完全相同的现象,还会花30页的篇幅去介绍一个基于NHibernate的资源库实现。说它好是因为面面俱到,把所有战略模式和战术模式都介绍了个遍,还有大量代码去帮你实现各种战术模式,可以说相当落地。

在学习的过程中,我常常翻阅IDDD中的相关章节进行补充阅读,发现当初晦涩难懂的概念慢慢变得容易起来。应用服务和领域服务不再傻傻分不清楚,不同的上下文映射方式也能在工作中找到对应的例子。对于DDD,感觉快要开始入门了。

与此同时,IDDD的精华版DDDD(Domain-Driven Design Distilled)也出版了。作者总结了过去几年在DDD方面的实战经验,将IDDD中的诸多内容精简升华。在很多概念处都标注了IDDD中的相关章节,可以算是IDDD的一个索引。

其中文版《领域驱动设计精粹》由ThoughtWorks同事覃宇和笪磊合作翻译。这是我读过的最良心的一本书籍,因为它包含了大量译者注解,解释了很多书中没有解释清楚的概念(毕竟是精粹本)。还有些有争议的观点,译者也毫不客气地给出自己的看法。

像这样注解超过原文的情况在其他书中是很少见的。每一处注解都倾注了译者的心血和精力,这背后势必包括大量资料的查阅和研究,而且很多解释都夹带了浓浓的ThoughtWorks特色,使得这样一本薄薄的书变得丰满充实起来。

如果你读书快的话,可能两个小时就读完这样一本书。但如果把原书和注解中的推荐文章和书籍读完,恐怕要一个月。我顺着书中的指引,找到了ThoughtWorks洞见上的所有DDD文章,读完之后,世界观崩塌了,感觉自己刚要入门就要放弃了。具体原因请参考链接中的文章。

其实,不管是DDD、IDDD、PPPDDD还是DDDD,讲的都是理论,充其量会附加一些作者杜撰的示例。相信我,光学习理论是没有用的,你必须将其应用于实践,在自己的真实项目里演练DDD。这时你才会发现,那些白纸黑字的概念,在读书时似乎搞清楚了,但一使用起来,反而更迷惑了。就像最基本的子域和限界上下文的关系问题,ThoughtWorks的首席咨询师肖然就和Vaughn Vernon的理解就相去甚远。到底该“信谁”?那就似乎更要通过实践来出真知了。


更多精彩洞见,请关注微信公众号:思特沃克

Share

从王安石变法看“规模化敏捷”

[摘要]

当前敏捷在大型组织中的规模化落地,某种程度上很像历史上的变法。回顾王安石变法的历史,它为什么会失败?对比敏捷的规模化实施,探讨“规模化敏捷”到底可不可取?以史为鉴,我们应该怎么做?


最近在阅读《罗辑思维》全集时,看到一章很有意思的内容,详细分析历史上的变法,其中王安石变法部分让我不断联想起规模化敏捷的实施。两相对比分析,有诸多启发。

一、历史回顾:王安石变法

大事记

王安石变法为什么会失败?

回顾王安石变法过程,会发现其初心很好,实施策略看似做得特别成功:

  • 变法初心:王安石改革之前,司马光问王安石怎么改革?王安石说他有一个方法叫“民不加赋而国用饶”。王安石认为,北宋国家贫苦的症结,不在于开支过多,而在于生产过少;农民之所以贫苦和不能从事生产,一方面是由于官僚富豪兼并了大量土地,另一方面是由于政府把繁重的徭役加在农民身上。因此,最好的理财富国之路,是依靠天下所有的劳动力去开发自然资源,是积极开源而不是消极节流。即不增加赋税,激发社会活力而让国家变得富强。
  • 实施策略:
    • 先定义蓝图:上《本朝百年无事札子》,谈改革之蓝图;
    • 获得高层支持:北宋皇帝宋神宗,本来久慕王安石之名,其变法之道得到了他的绝对支持,他常常跟周围的人说,他跟王安石就是一个人;
    • 清除反对力量:王安石在获得高层的绝对支持后,对变革有异见的大臣比如欧阳修、司马光进行清除,扫清障碍;
    • 全面推广:在蓝图获准、清除变法异己之后,快节奏地全面推广。

然而,结果却失败了。为什么呢?浅析一下,原因可能是:

1. 目标与执行上的不匹配

历史上的变法,可以分为两类:追求效率的“效率型变法”和追求活力的“活力型变法”。

  • “效率型变法”:效率优先、集中财富但不制造资源和财富增量。简单来说,就是围绕单一目标展开,国家要达到这个目标,不惜一切代价集中所有资源都要实现,比如商鞅变法,春秋时代层层分封的财富分配体系被商鞅全部拆掉,他把国家的每一个老百姓、每一粒粮食都镶嵌在国家这个战争机器上,所以秦国变得非常富强。
  • “活力型变法”:激发活力、制造财富和资源增量。这种变法会同时考虑多个目标比如朝廷财源比较丰沛、老百姓安居乐业、市场繁荣稳定、军队能够有战斗力、政府高效廉洁等;在实施时不是调整存量分配,而是想法设法制造财富和资源增量。比如1933年到1941年期间,由美国第32任总统富兰克林·罗斯福发起的罗斯福新政,他围绕经济、农业与工业、社会保障以及政策制度等几个大的维度展开,后人用核心3R来总结,即救济(Relief)、复兴(Recovery)和改革(Reform),也称三R新政。最终,美国经济复苏,政治制度上建立了以总统为中心的三权分立格局,人民生活得到极大改善。

王安石变法,初心上是“追求活力”、有多重目标的,但实施上却采取了“效率优先”的方法,核心是依靠权力把效率推进下去。“效率型变法”要想成功,有一个天然的前提,就是先有蓝图,然后集中快速施工。对一个单目标系统,预先设定一个“正确的”蓝图尚且挑战重重,如果是针对一个多目标系统,几乎是不可能的。这种目标与执行策略上的不匹配,成为失败的原因之一。

2. 低估了制度成本

其实王安石的变法,从制度设计角度是非常好的,比如青苗法的初衷就很好:给老百姓提供低息贷款,避免去借高利贷,以保护和赈济民户。但问题出在制度的实施是表面上附和但内心未必接纳的各级官吏来推进的。最后变成什么了呢?那就是领导意志。比如当时有一个小官叫做邓绾,为了巴结神宗,当听到神宗称赞王安石为“当今之古人”后,瞬间明白领导意思,就神乎其神地夸王安石变法各种好,虽然他不一定懂新法但却得到了重用。这些变法的落地执行者,为了达到目标或超额完成目标,强制摊派老百姓贷款。最后,实际结果适得其反,不仅没有降低老百姓的压力,反而变成了一种变相的赋税。所以,良好的蓝图在实施过程中遇到了制度成本——执行变法的人,没有理解其真实意图,只是围绕既定KPI开展工作,结果自然不好

3. 忽视长期、可持续的变革力量

任何一种变革都不可能一次性成功,新法也可能会失败。如果失败了,那些反对派一定会站出来攻击王安石。所有效率型改革,只要失去高层支持,无论是主动的还是被动的,出路只有一条,那就是失败。宋神宗一死,继位的高太后一上台就把王安石搞下台,新法完全没有了机会,退出历史舞台!王安石阵营在变法期间,并没有能够培养出持续推动变革的力量,比如王安石卸任时推荐了他信任的吕惠卿继任,这个人不但没有继承变法大志,反而落井下石,跟邓绾一起诬陷王安石叛乱。因此,后面即使没有高太后和司马光,变法也难以为继。

二、敏捷的规模化落地与变法实施

敏捷(Agile)是2001年由17位资深软件领域专家们一起发起的针对软件开发工作价值观的倡议。作为一种软件开发理念,与之伴随出现了很多实践框架和方法,如Scrum、Kanban和XP。而这些方法目前已经逐步成为了我们软件开发的实施标准,类似持续集成(Continuous Integration)这种10年前被大家认为是“极限”的方法,现在也已经成了开发团队的一个标配。但在很多大型组织里,敏捷的大规模应用仍然是一个非常有挑战性的任务。软件自身的多样性和这个行业的高速发展,造成了敏捷方法落地的种种挑战。

1. 敏捷的规模化落地,本质是个多目标系统

敏捷的初心,即面向市场和商业模式变化如何提升业务响应力,为用户带来真正有价值、高质量的产品。在整个组织里落地敏捷是一个多目标系统工程,目标至少包含发布高质量、满足用户需求的产品,打造有创造力的文化,建立高响应力的团队等。这带来的第一个挑战就是“蓝图的设定”。没有人可以预先为这个多目标系统设定清晰而正确的蓝图。

最近几年在国际银行届比较知名的数字化转型成功案例非BBVA(西班牙对外银行Banco Bilbao Vizcaya Argentaria)莫属。作为一家拥有6520亿欧元资产的全球银行集团,2007年应对全球金融危机时,BBVA开启其创新旅程,对集团进行数字化革新。尽管拥有上层认可及远见蓝图,BBVA的数字化转型之旅也并非按部就班的沿着蓝图前行,其转型过程大致经历了三个阶段:IT内部创新、扩大团队与银行业务发展重点的创新、数字银行分支匹配敏捷项目管理。一路上尝试不同的结构和方法,通过实验不断调整,最终成为数字化转型的成功典范。

2. 敏捷的规模化落地,是追求活力而不是追求效率

正如前文所述,敏捷的规模化落地,其核心还是围绕敏捷的核心价值观和原则展开,只不过参与的产品更多、人员更多而已。它追求的不是单一目标 —— 规模化,而是价值、质量与其他约束条件的调和所带来的多方优化结果。

笔者曾经参与过一家大型外资银行的敏捷转型辅导,其高管层的目标就是在一年内针对其全球六七万人的IT团队实施敏捷转型,所有部门,包括PMO以及外部教练都要围绕这一目标展开。大部分时间花在了制定各种对团队进行评估的模型上,而团队级别的敏捷实践深入辅导不足,团队对于敏捷理解程度有限。一年之后,形式上虽然绝大部分都参与过敏捷培训,或者敏捷教练辅导,但是对于产品质量和价值的提升非常有限。当这种疾风扫落叶的培训活动结束后,能够保留下来真真正正实施敏捷的团队已经屈指可数,大部分又回到了过去的开发方式。

上面两点,决定了敏捷的规模化落地,如果采用“王安石变法式”的实施方式,必然会失败。

三、规模化敏捷框架与王安石变法

当前大量组织级的敏捷转型需求,催生出了如SAFe、LeSS这样的“规模化敏捷”框架。如果详细研究SAFe的实施过程可以看到,它有完整的白皮书、官网,市面上有各种培训和认证,特别是SAF4.5已经给出了4个经典配置以及10个步骤的实施路线图,看似为组织给出了一个“清晰而正确”的蓝图。

SAFe的蓝图和实施路线图看似很完美,但在落地过程中会遇到诸多挑战,一如一开始看起来很美的“王安石变法”。

1. 追求活力的多目标,实施时变形为单一目标

在实施SAFe的时候,容易想到在组织内部去寻找宋神宗这样的人,这并没有错。但是,根据组织心理学家William Schneider提出的组织文化理论:其中,95%的商业组织都属于控制型文化,这种组织文化强调的是上传下达,领导意志。

(文化四象限)

因此,如果在组织内部发起变革,很容易变成目标问题。也就是王安石遇到宋神宗之后面临的挑战。本来是一个追求活力的变革,最后实施时变成了效率型方式。

2. 因为制度成本,忘记真实意图,仅仅围绕效率指标

前面讲到王安石变法过程中的制度成本问题,在规模化敏捷实施时也普遍存在。无论组织是自我变革,还是请外部教练,很容易变成在领导意志下围绕一个“蓝图”按部就班地展开。而且为了考核规模化的进展,就会设立规模化的指标,比如组织内部百分之多少的团队纳入规模化敏捷运作框架等。再加上出发点就是错误的,失之毫厘谬以千里,离变革成功就越来越远。不难想象,跟王安石变法类似,在实施过程中为了追求规模化的效率指标,往往忽视内功修炼。

笔者刚经历的一个实际案例,某家全球性企业正在实施SAFe,总部请了一名资深的SAFe教练,飞到该公司各个地区负责实施和辅导。当我们作为一线敏捷教练后续进入对中国区团队实施辅导时,某个团队Scrum Master(SM)有一天问我,Sprint长度是不是必须两周?因为他们团队之前一直是三周一个Sprint。我当时很奇怪,问他为什么这么问?他回答说来自Global的教练说了,Sprint必须两周,要不然就不对。我当时听到觉得很震惊,竟然僵化到如此程度!作为一线的SM有此疑惑竟然得不到有说服力的答疑,那么执行过程中僵化就在所难免。

3. 忽视人才和生机型文化,一旦推进不力,一夜回到解放前

当变革推进不力时,反对派的反扑就来了,一夜回到解放前的实际案例不胜枚举。再加上大公司的组织结构重组(re-org)是经常的事,如果支持者不在其位,那么围绕其建立的变革实施人员的动力就会大打折扣。前面提到的大型外资银行的例子,之所以敏捷转型实施一年多以后就没有继续,其中一个很大的原因是全球的高管团队进行了更换,新上任的CXO们并没有认为敏捷转型有价值,所以放弃了之前转型带来的初步成果。更重要的是,在过去一年多的转型过程中,大部分精力并没有放到转型人才的持续培养上,所以当高管团队决定不再投资外部教练,内部的转型力量也没有跟上,变革也就不了了之。

所以,SAFe的蓝图和实施路线图看似很完美,但如同“王安石变法”,注定难以成功。

四、敏捷的规模化落地,如何破?

敏捷的规模化落地,本质是个Complex问题(Cynefin模型)。规模化敏捷框架的最大问题是将一个Complex领域的问题当成Complicated或者Simple领域问题来处理。一个多目标问题,其实是没有Good或Best practices的,唯一有指导意义的是一些价值观和基本原则。对待Complex领域问题,要采取探索-感知-响应模式,快速探索,感知问题的存在,采取行动响应,然后及时调整,对应问题的实践在这个过程中则会涌现出来,而不应该按照一个框架蓝图就开始配置实施。

比如前面提到的BBVA的案例:

  1. 远大愿景:基于对行业趋势的判断,BBVA清楚地认识到,客户的地位在提升,监管的要求趋严,新技术的涌现让银行业面临着前所未有的竞争。BBVA开始以增强与客户的关系作为愿景的转型之路,战略目标定位为成为“数字化时代最好的银行”。而且,基于此愿景制定了6大优先级战略引领集团转型,涵盖了客户体验新标准、驱动数字销售、新商业模式等。所以可见,他们要平衡的是一个多目标系统,既包含用户也包含技术与商业模式等。
  2. 快速行动:BBVA采用实验与学习的方法,将员工、高校研究机构、创新机构、BBVA创新中心、BBVA创业公司比赛、Innova黑客马拉松挑战赛、BBVABeta测试器、BBVA风投以及收购和合作伙伴融入整个创新生态圈,通过各种方式将最新成果快速应用于为客户创造价值,为企业创造收入。
  3. 及时调整:BBVA认为企业应先设立其创新计划并随时准备根据变化对其进行调整,而非盲目的执着于最初计划。比如BBVA的创新项目选择非常灵活,设立指导委员会并根据优先级选择项目,灵活的预算编制以及敏捷的实施,在实施过程中学习,及时调整并探索。

所以,变革成功的哲学是要有一个远大的愿景 —— Think Big;赶紧行动,摸着石头感知河水的深浅、流速,找到可以过河的方案 —— Start Small;根据实验结果,持续学习并改进 —— Learn Fast。

总结

当我们要在一家企业推动敏捷时,首先它是一个活力型变革,多目标问题,而且是一个Complex领域问题,这不是靠套用一种 “规模化敏捷” 框架就能解决的。不要忘记敏捷的初衷,切忌把规模化本身当成目标。当我们忘记“蓝图”,把握“Think BIg, Start Small, Learn Fast”这样的基本原则,更容易找到适合特定企业、组织的敏捷实施方式,比如肖然在《忘记“规模化敏捷”》里所说的“建立持续改进的内部力量、系统思考整个开发过程,以及为创新建立安全试错环境”等。


更多精彩商业洞见,请关注微信公众号:ThoughtWorks商业洞见

Share